偶数を数えるシェル関数の間違い


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
あなたのシェバン '#!/ usr / bin / bash -x'に書き込むと、正確に何が起こるかがわかります。
ジアジス

宿題だと教えてくれて、そして助けに値するほど一生懸命取り組んだことで+1。
ジョー

回答:


20

ループ内$#で($elementに置き換えるのを忘れましたfor

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. シバン:/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変数を省略できます(読みやすくするため、IMHO)。

  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
ターサーループボディを探している場合は、を使用しますsum=$((sum + 1 - element % 2))
デビッドフォースター

1
@DavidFoerster Terserおよび読みにくい。;-)
コンラッドルドルフ

@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なしでもこれを実行できることを指摘しました。

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

これは、evensすべてのオッズを除外して呼び出されるリストを作成し、そのリストの長さを返すことで機能します。


興味深いアプローチ。もちろん、これは3.0偶数としてカウントされます。質問は数字がどのような形になるかについて正確ではありませんでした。
G-Manが「Reinstate Monica」と言う

@ G-Manはbashが浮動小数点演算をサポートしていないため、整数を仮定しても安全
です-muru

質問では「偶数」(および暗黙的に「奇数」)の数値に言及しているため、整数について話していると見なすことは多少安全です。ユーザーが使用することが期待されるツールの機能と制限から、質問に関する結論を導き出すことがどのように有効であるかわかりません。覚えておいてください:質問はこれが課題であると言っています—学校にとっては、これは仕事から生まれるにはささいなことだからです。学校の先生はいくつかのクレイジーなことを思いつきます。
G-マンは「元に戻すモニカ言う

2
空白を含む要素がないと想定できる場合、Bashはリストを独自にフィルター処理できます。配列初期化子内で、文字列の末尾での一致を要求する${/%/}ために@配列を使用して、空の文字列で置き換えられた奇数の引数リストを展開します。カウントを印刷します。:定義し、それを実行するためのワンライナーとして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。うーん、bash関数を投稿できるcodegolfの質問があるのだろうか:P
Peter Cordes
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.