Erro na função shell para contar números pares

12

Para uma tarefa, tenho que escrever uma função que imprima o número de números pares quando fornecida com uma sequência de números.

Eu usei o código que usei para uma tarefa anterior (para imprimir 1 quando um número era par e 0 quando o número era ímpar)

Meu problema agora é que minha função continua imprimindo 0 . O que estou fazendo errado?

Aqui está meu script:

#!/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
}
    
por Jedidja 24.04.2018 / 09:19

3 respostas

20

Você acabou de esquecer de substituir $# por ( $ ) element no loop 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
}

Agora, para testar a função:

$ 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
    
por dessert 24.04.2018 / 09:34
16

@dessert encontrou o problema central, vou dar uma revisão do código:

  1. O shebang: Não há /usr/bin/bash no Ubuntu. É /bin/bash .
  2. É bom que você tenha declarado sum local e evitou poluir o namespace da variável fora da função. Além disso, você pode declarar uma variável inteira usando a opção -i :

    local -i sum=0
    
  3. Sempre cite suas variáveis (e parâmetros)! Não é necessário neste script, mas um hábito muito bom para entrar:

    for element in "$@"
    do
    

    Dito isso, você pode omitir o in "$@" aqui:

    for element
    do
    

    Quando in <something> não é fornecido, o loop for faz um loop implícito nos argumentos. Isso pode evitar erros como esquecer as citações.

  4. Não há necessidade de calcular e verificar o resultado. Você pode fazer diretamente o cálculo no if :

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

    (( ... )) é o contexto aritmético . É mais útil que [[ ... ]] para realizar verificações aritméticas e, além disso, você pode omitir as variáveis $ antes (o que facilita a leitura, IMHO).

  5. Se você mover a parte de verificação contínua para uma função separada, isso poderá melhorar a legibilidade e a reutilização:

    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"
    }
    
por muru 24.04.2018 / 09:52
3

Não tenho certeza se você está aberto a outras soluções. Também não sei se você pode usar utilitários externos, ou se você está puramente limitado a bash builtins. Se você pode usar grep , por exemplo, sua função pode ser muito mais simples:

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

Isso coloca cada inteiro de entrada em sua própria linha e, em seguida, usa grep para contar as linhas que terminam em um dígito par.

Update - @PeterCordes apontou que podemos até fazer isso sem o grep - apenas o puro bash, desde que a lista de entrada contenha apenas inteiros bem formados (sem pontos decimais):

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

Isso funciona criando uma lista chamada evens , filtrando todas as probabilidades e retornando a duração dessa lista.

    
por Digital Trauma 25.04.2018 / 01:08