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

为什么使用 && 比 if...fi 快 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))
    [ $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"

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

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"

有没有既能保证块的清晰度又不会显得缓慢的方法?

更新(美国东部时间 2015 年 1 月 4 日上午 10:40)

很好的反馈!我现在正在使用以下内容。还有其他反馈意见吗 ?

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"

答案1

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

if ([ $remainder == 0 ] && [ $is_prime == true ]); then

只需删除括号即可

if [ $remainder == 0 ] && [ $is_prime == true ]; then

如果您想对命令进行分组,可以使用语法来执行此操作当前的壳:

if { [ $remainder == 0 ] && [ $is_prime == true ]; }; then

(尾部分号是必需的,请参阅手册

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

为了便于阅读,我会更改很长的行

[ $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

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

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

答案2

我认为你在你的职责上工作得太辛苦了。考虑:

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

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

它绝对在做这项工作。它只需要很少的其他东西就可以近似你的输出:

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

只是这样做而不是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

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

您的子 shell 问题将在大多数 shell 中发生,但它是通过远的,在贝壳中最尖锐bash。我交替做

( [ "$div" -gt "$num" ] ) && ...

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

相关内容