为什么使用 && 比 if...fi 快 75 倍以及如何使代码更清晰

Mic*_*ant 38 shell shell-script

我有以下工作代码:

largest_prime=1
for number_under_test in {1..100}
do
  is_prime=true
  factors=''
  for ((divider = 2; divider < number_under_test-1; divider++));
  do
    remainder=$(($number_under_test % $divider))
    [ $remainder == 0 ] && [ is_prime ] && is_prime=false && factors+=$divider' '
  done
  [ $is_prime == true ] && echo "${number_under_test} is prime!" || echo "${number_under_test} is NOT prime (factors= $factors)"  [ $is_prime == true ] && largest_prime=$number_under_test
done
printf "\nLargest Prime= $largest_prime\n"
Run Code Online (Sandbox Code Playgroud)

这段代码运行得很快是 0.194 秒。然而,我发现它&& is_prime= false有点难以阅读,它看起来(在未经训练的眼睛看来)好像是在测试而不是被设置,这就是它的作用。所以我尝试将其更改&&为 anif...then并且这有效 - 但在 14.48 秒时慢了 75 倍。在较高的数字上最为明显。

largest_prime=1
for number_under_test in {1..100}
do
  is_prime=true
  factors=''
  for ((divider = 2; divider < number_under_test-1; divider++));
  do  
    remainder=$(($number_under_test % $divider))
    if ([ $remainder == 0 ] && [ $is_prime == true ]); then
      is_prime=false
      factors+=$divider' '
    fi  
  done
  [ $is_prime == true ] && echo "${number_under_test} is prime!" || echo "${number_under_test} is NOT prime (factors= $factors)"  [ $is_prime == true ] && largest_prime=$number_under_test
done  
printf "\nLargest Prime= $largest_prime\n"
Run Code Online (Sandbox Code Playgroud)

有没有块的清晰度而不是缓慢的?

更新(1/4/2015 10:40am EST)

很棒的反馈!我现在正在使用以下内容。还有别的反馈吗 ?

largest_prime=1
separator=' '
for number_under_test in {1..100}; {
  is_prime=true
  factors=''
  for ((divider = 2; divider < (number_under_test/2)+1; divider++)) {
    remainder=$(($number_under_test % $divider))
    if [ $remainder == 0 ]; then
      is_prime=false
      factors+=$divider' '
    fi
  } 
  if $is_prime; then
    printf "\n${number_under_test} IS prime\n\n"
    largest_prime=$number_under_test
  else
    printf "${number_under_test} is NOT prime, factors are: "
    printf "$factors\n"
  fi
}
printf "\nLargest Prime= $largest_prime\n"
Run Code Online (Sandbox Code Playgroud)

gle*_*man 67

那是因为你每次都在生成一个子 shell:

if ([ $remainder == 0 ] && [ $is_prime == true ]); then
Run Code Online (Sandbox Code Playgroud)

去掉括号就可以了

if [ $remainder == 0 ] && [ $is_prime == true ]; then
Run Code Online (Sandbox Code Playgroud)

如果你想对命令进行分组,在当前的shell 中有这样的语法:

if { [ $remainder == 0 ] && [ $is_prime == true ]; }; then
Run Code Online (Sandbox Code Playgroud)

(需要尾随分号,请参阅手册

请注意,[ is_prime ]这与[ $is_prime == true ]: 不同,您可以简单地$is_prime(不带括号)将其编写为调用 bash 内置命令truefalse命令。
[ is_prime ]是一个带有一个参数的测试,字符串“is_prime”——当[给定一个参数时,如果参数非空,则结果为成功,并且该文字字符串始终非空,因此始终为“真”。

为了可读性,我会改变很长的一行

[ $is_prime == true ] && echo "${number_under_test} is prime!" || echo "${number_under_test} is NOT prime (factors= $factors)"  [ $is_prime == true ] && largest_prime=$number_under_test
Run Code Online (Sandbox Code Playgroud)

if [ $is_prime == true ]; then
  echo "${number_under_test} is prime!"
else 
  echo "${number_under_test} is NOT prime (factors= $factors)"
  # removed extraneous [ $is_prime == true ] test that you probably
  # didn't notice off the edge of the screen
  largest_prime=$number_under_test
fi
Run Code Online (Sandbox Code Playgroud)

不要低估空白以提高清晰度。

  • bash 实现了 `[`,它是一个内置函数。在 shell 提示符下,输入 `type -a [` 和 `help [` (2认同)
  • @glennjackman [`command -v`](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html) 是另一个不错的 `which` 替代方案;另见[此处](http://unix.stackexchange.com/a/47406/5092)。 (2认同)

mik*_*erv 9

我认为你在你的这个功能上工作得太努力了。考虑:

unset num div lprime; set -- "$((lprime=(num=(div=1))))"
while [     "$((     num += ! ( div *= ( div <= num   ) ) ))" -eq \
            "$((     num *=   ( div += 1 )   <= 101   ))" ]    && {
      set   "$(( ! ( num %      div )         * div   ))"     "$@"
      shift "$(( !    $1 +    ( $1 ==  1 )    *  $#   ))"
}; do [ "$div" -gt "$num" ] && echo "$*"      
done
Run Code Online (Sandbox Code Playgroud)

Shell 算法非常有能力自行评估整数条件。它很少需要太多的测试和/或外部作业。这个while循环很好地复制了您的嵌套循环:

它没有打印那么多,当然,我没有写那么多,但是,例如,将上限设置为 16 而不是上面写的 101 并且......

2
3
4 2
5
6 3 2
7
8 4 2
9 3
10 5 2
11
12 6 4 3 2
13
14 7 2
15 5 3
Run Code Online (Sandbox Code Playgroud)

它肯定在做这项工作。并且它几乎不需要其他东西来近似您的输出:

...
do [ "$div" -eq "$num" ] && shift &&
   printf "$num ${1+!}= prime.${1+\t%s\t%s}\n" \
          "factors= $*"                        \
          "lprime=$(( lprime = $# ? lprime : num ))"
done
Run Code Online (Sandbox Code Playgroud)

只是这样做而不是echo...

1 = prime.
2 = prime.
3 = prime.
4 != prime.     factors= 2      lprime=3
5 = prime.
6 != prime.     factors= 3 2    lprime=5
7 = prime.
8 != prime.     factors= 4 2    lprime=7
9 != prime.     factors= 3      lprime=7
10 != prime.    factors= 5 2    lprime=7
11 = prime.
12 != prime.    factors= 6 4 3 2        lprime=11
13 = prime.
14 != prime.    factors= 7 2    lprime=13
15 != prime.    factors= 5 3    lprime=13
Run Code Online (Sandbox Code Playgroud)

这适用于busybox. 它非常便携、快速且易于使用。

您的子外壳问题将在大多数外壳中发生,但到目前为止,它在bash外壳中最为严重。我交替做

( [ "$div" -gt "$num" ] ) && ...
Run Code Online (Sandbox Code Playgroud)

...以及我在上面的几个 shell 中为 101 的上限编写它的方式,并且dash在 0.017 秒内没有 subshel​​l 和在 1.8 秒内完成了 subshel​​l。busybox.149 和 2、zsh .149 和 4、bash.35 和 6,以及ksh93.149 和 .160。ksh93不会像其他 shell 那样分叉子 shell。所以也许问题不在于子外壳,而在于外壳