Ошибка в функции оболочки для подсчета четных чисел

Вы можете использовать функцию pure-ftpd PureDB для создания входа ftp только для службы pure-ftpd.

12
задан 24 April 2018 в 11:06

6 ответов

Вы просто забыли заменить $# на $) 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
20
ответ дан 17 July 2018 в 16:16

Я не уверен, что вы открыты для других решений. Кроме того, я не знаю, можете ли вы использовать внешние утилиты, или если вы ограничены исключительно встроенными bash. Например, если вы можете использовать grep, ваша функция может быть намного проще:

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

Это помещает каждое целое число ввода в свою собственную строку, а затем использует grep для подсчета строк

Update - @PeterCordes отметил, что мы можем даже сделать это без grep - просто чистый bash, если список ввода содержит только хорошо сформированный целые числа (без десятичных точек):

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

Это работает, создавая список с именем evens, отфильтровывая все коэффициенты, а затем возвращая длину этого списка.

3
ответ дан 17 July 2018 в 16:16

@dessert нашел основную проблему, я дам некоторый обзор кода:

Shebang: В Ubuntu нет /usr/bin/bash. Это /bin/bash. Хорошо, что вы объявили sum local и избежали загрязнять пространство имен переменных вне функции. Кроме того, вы можете объявить его целочисленной переменной с помощью опции -i: local -i sum=0 Всегда указывайте свои переменные (и параметры)! В этом скрипте нет необходимости, но очень хорошая привычка: for element in "$@" do Здесь вы можете опустить in "$@" здесь: for element do Когда in <something> не задано, цикл for неявно перебирает аргументы. Это может избежать ошибок, таких как забывание котировок. Нет необходимости вычислять, а затем проверять результат. Вы можете непосредственно выполнить расчет в if: if (( (element % 2) == 0 )) then ((sum = sum + 1)) fi (( ... )) - это арифметический контекст. Это более полезно, чем [[ ... ]] для выполнения арифметических проверок, и дополнительно вы можете опустить $ перед переменными (что упрощает чтение, IMHO). Если вы перенесли элемент проверки четности в отдельную функцию, это может улучшить читаемость и повторное использование: 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" }
16
ответ дан 17 July 2018 в 16:16

Вы просто забыли заменить $# на $) 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
20
ответ дан 23 July 2018 в 17:10

Я не уверен, что вы открыты для других решений. Кроме того, я не знаю, можете ли вы использовать внешние утилиты, или если вы ограничены исключительно встроенными bash. Например, если вы можете использовать grep, ваша функция может быть намного проще:

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

Это помещает каждое целое число ввода в свою собственную строку, а затем использует grep для подсчета строк

Update - @PeterCordes отметил, что мы можем даже сделать это без grep - просто чистый bash, если список ввода содержит только хорошо сформированный целые числа (без десятичных точек):

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

Это работает, создавая список с именем evens, отфильтровывая все коэффициенты, а затем возвращая длину этого списка.

3
ответ дан 23 July 2018 в 17:10
  • 1
    Интересный подход. Конечно, это будет считать 3.0 четным числом; вопрос не был точен о том, какую форму будут занимать цифры. – G-Man 25 April 2018 в 03:27
  • 2
    @ G-Man, так как bash не поддерживает арифметику с плавающей запятой, можно предположить, что целые числа – muru 25 April 2018 в 05:02
  • 3
    Поскольку в вопросе упоминаются «четные» (и, неявно, «нечетные») числа, можно с уверенностью предположить, что речь идет о целых числах. Я не вижу, как правильно делать выводы о вопросе из возможностей и ограничений инструмента, который предполагается использовать пользователю. Помните: в вопросе говорится, что это задание - я думаю, для школы, потому что это слишком тривиально, чтобы быть от работы. Школьные учителя придумывают некоторые сумасшедшие вещи. – G-Man 25 April 2018 в 05:41
  • 4
    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 (по отдельным линиям) – Peter Cordes 25 April 2018 в 13:24
  • 5
    Вероятно, у ZSH есть что-то, чтобы должным образом отфильтровать список, вместо того, чтобы в зависимости от разбиения слова перестроить новый массив (я был немного удивлен, что bash этого не делал, только применяя скалярное расширение строки для каждого элемента). Для больших списков я не удивлюсь, если grep на самом деле быстрее, чем bash. Хм, я задаюсь вопросом, есть ли какой-то вопрос, где я могу опубликовать эту функцию bash: P – Peter Cordes 26 April 2018 в 01:30

@dessert нашел основную проблему, я дам некоторый обзор кода:

Shebang: В Ubuntu нет /usr/bin/bash. Это /bin/bash. Хорошо, что вы объявили sum local и избежали загрязнять пространство имен переменных вне функции. Кроме того, вы можете объявить его целочисленной переменной с помощью опции -i: local -i sum=0 Всегда указывайте свои переменные (и параметры)! В этом скрипте нет необходимости, но очень хорошая привычка: for element in "$@" do Здесь вы можете опустить in "$@" здесь: for element do Когда in <something> не задано, цикл for неявно перебирает аргументы. Это может избежать ошибок, таких как забывание котировок. Нет необходимости вычислять, а затем проверять результат. Вы можете непосредственно выполнить расчет в if: if (( (element % 2) == 0 )) then ((sum = sum + 1)) fi (( ... )) - это арифметический контекст. Это более полезно, чем [[ ... ]] для выполнения арифметических проверок, и дополнительно вы можете опустить $ перед переменными (что упрощает чтение, IMHO). Если вы перенесли элемент проверки четности в отдельную функцию, это может улучшить читаемость и повторное использование: 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" }
16
ответ дан 23 July 2018 в 17:10
  • 1
    Если вы ищете тело тесного контура, используйте sum=$((sum + 1 - element % 2)). – David Foerster 24 April 2018 в 11:39
  • 2
    @DavidFoerster Terser и менее читабельна. ;-) – Konrad Rudolph 24 April 2018 в 13:07
  • 3
    @DavidFoerster: Я думал, что вам нужно просто использовать результат mod-2 напрямую. (Или лучше, &1, чтобы проверить низкий бит, если это более читаемо для вас). Но мы можем сделать его более читаемым: sum=$((sum + !(element&1) )) использовать логический обратный вместо +1 - condition. Или просто подсчитайте нечетные элементы с ((odd += element&1)), а в конце напечатайте с помощью echo $(($# - element)), потому что even = total - odd. – Peter Cordes 25 April 2018 в 13:11
  • 4
    Хорошая работа, и хорошо указать на мелочи, которые новички пропускают, например. local -i и sum++. – Paddy Landau 1 May 2018 в 15:06

Другие вопросы по тегам:

Похожие вопросы: