错误的shell函数来计算偶数


12

对于一项作业,我必须编写一个函数,该函数在提供数字序列时会打印偶数数字。

我使用了以前分配的代码(1在数字为偶数和0数字为奇数时打印)

我现在的问题是我的功能一直在打印0。我究竟做错了什么?

这是我的脚本:

#!/usr/bin/bash
# File: nevens.sh

# Write a function called nevens which prints the number of even numbers when provided with a sequence of numbers.
# Check: input nevens 42 6 7 9 33 = output 2

function nevens {

        local sum=0

        for element in $@
        do
                let evencheck=$(( $# % 2 ))
                if [[ $evencheck -eq 0 ]]
                then
                        let sum=$sum+1
                fi
        done

        echo $sum
}

2
在您的shebang中写上“#!/ usr / bin / bash -x”,那么您将看到实际发生的情况。
Ziazis

+1是为了告诉我们这是家庭作业-并为此付出了足够的努力值得给予帮助。

Answers:


20

您只是忘了在循环中替换$#为($):elementfor

function nevens {
  local sum=0
  for element in $@; do
    let evencheck=$(( element % 2 ))
    if [[ $evencheck -eq 0 ]]; then
      let sum=sum+1
    fi
  done
  echo $sum
}

现在测试一下功能:

$ nevens 42 6 7 9 33
2
$ nevens 42 6 7 9 33 22
3
$ nevens {1..10..2} # 1 to 10 step 2 → odd numbers only
0
$ nevens {2..10..2} # 2 to 10 step 2 → five even numbers
5

17

@dessert找到了核心问题,我将进行一些代码审查:

  1. shebang:/usr/bin/bashUbuntu中没有。是/bin/bash
  2. 最好声明sum local,避免污染函数外部的变量名称空间。此外,您可以使用以下-i选项将其声明为整数变量:

    local -i sum=0
  3. 始终引用变量(和参数)!该脚本中没有必要,但要养成以下习惯:

    for element in "$@"
    do
    

    也就是说,您可以在in "$@"此处省略:

    for element
    do
    

    如果in <something>未指定,则for循环隐式循环遍历参数。这样可以避免诸如忘记引号之类的错误。

  4. 无需计算,然后检查结果。您可以直接在中进行计算if

    if (( (element % 2) == 0 ))
    then
        ((sum = sum + 1))
    fi
    

    (( ... ))算术上下文。它比[[ ... ]]执行算术检查更有用,此外,您可以省略$before变量(恕我直言,这更易于阅读)。

  5. 如果将偶校验部分移动到单独的函数中,则可能会提高可读性和可重用性:

    function evencheck
    {
        return $(( $1 % 2 ))
    }
    function nevens
    {
        local -i sum=0
        for element
        do
            # `if` implicitly checks that the returned value/exit status is 0
            if evencheck "$element"
            then
                (( sum++ ))
            fi
        done
        echo "$sum"
    }
    

1
如果您正在寻找一个terser loop body,请使用sum=$((sum + 1 - element % 2))
David Foerster

1
@DavidFoerster Terser,可读性较差。;-)
Konrad Rudolph

@DavidFoerster:我也有同样的想法,那就是您应该直接使用mod-2结果。(或者更好的方法是,&1检查低位是否更易读)。但是,我们可以使其更具可读性:sum=$((sum + !(element&1) ))使用布尔反函数代替+1 - condition。或者只用计数奇数元素((odd += element&1)),最后用计数echo $(($# - element)),因为even = total - odd
彼得·科德斯

做得好,并指出一些初学者想念的小东西,例如local -isum++
帕迪·兰道

4

我不确定您是否愿意接受其他解决方案。另外我也不知道您是否可以使用外部实用程序,或者您是否仅限于bash内置函数。grep例如,如果可以使用,则您的函数可能会简单得多:

function nevens {
    printf "%s\n" "$@" | grep -c '[02468]$'
}

这会将每个输入整数放在自己的行上,然后用于grep对以偶数位结尾的行进行计数。


更新-@PeterCordes指出,即使输入列表中仅包含格式正确的整数(不带小数点),我们甚至也可以不使用grep-纯bash来执行此操作:

function nevens{
    evens=( ${@/%*[13579]/} )
    echo "${#evens[@]}"
}

这是通过创建一个evens通过过滤所有赔率来调用的列表然后返回该列表的长度来实现的。


有趣的方法。当然,这将算3.0为偶数。关于数字采用什么形式的问题并不确切。
G-Man说'Reinstate Monica''Apr

@ G-Man,因为bash不支持浮点运算,所以可以安全地假定整数
muru

由于问题中提到的是“偶数”(隐式为“奇数”),因此可以肯定地说它是在谈论整数。我不认为从期望用户使用的工具的功能和局限性得出结论,这是多么有效。记住:这个问题说这是一项任务,我想上学,因为这太微不足道了,无法工作。学校老师想出了一些疯狂的事情。
G-Man说'Reinstate Monica''Apr

2
如果我们假设没有元素包含空格,则Bash可以自行过滤列表:在数组初始化器中使用${/%/}@数组上使用,以要求在字符串末尾进行匹配的情况下,将arg列表扩展为用空字符串替换的奇数。打印计数。作为一个班轮定义和运行它foo(){ evens=( ${@/%*[13579]/} ); echo "${#evens[@]} even numbers"; printf "%s\n" "${evens[@]}"; }; foo 135 212 325 3 6 3 4 5 9 7 2 12310。包括实际打印列表以进行调试。印刷品5 even numbers 212 6 4 2 12310(于9
彼得·科德斯

1
ZSH大概可以正确过滤列表,而不是依靠分词来重建新数组(bash没想到,我有点惊讶;只对每个元素应用了标量字符串扩展内容)。对于大型列表,如果grep实际上比快,我不会感到惊讶bash。嗯,我想知道是否有一个codegolf问题可以在其中发布该bash函数:P
Peter Cordes,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.