if ... fi보다 && 75 배 더 빠른 이유와 코드를 더 명확하게 만드는 방법


38

다음 작업 코드가 있습니다.

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조금 읽기가 어렵다는 것을 알았고 그것이 설정되어있는 것이 아니라 테스트되고있는 것처럼 (훈련되지 않은 눈으로) 보일 수 있습니다. 나는 변경 시도 그래서 &&if...then14.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"

속도가 느려지지 않고 블록의 선명도가 있어야합니까?

업데이트 (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
주석에서 스크립트를 실행 Largest Prime= 100하면 내 컴퓨터에 스크립트가 인쇄 됩니다.
Giulio Muscarello

3
또한 각주에, 당신이 효율성에 관심이 있다면, 이것을 개선하는 간단한 방법 number_under_test/2은 최대 대신 반복하는 것입니다 number_under_test-1: 숫자 n의 인수가 n / 2보다 크지 않으므로 여전히 모든 것을 찾을 수 있습니다 이를 수행하여 비 프라임 숫자에 대한 요인. (또한 소수 테스트 에만 관심이 있다면 최대 sqrt (n)까지 반복하면 충분하지만 Bash에는 제곱근을 계산할 수있는 기본 제공 함수가 없습니다.
Malte Skoruppa

매트, 좋은 지적 (+1). 유일한 변경 사항은 4 번에는 효과가 없었기 때문에 (number_under_test/2)+1허용해야했습니다
Michael Durrant

1
업데이트 된 버전에서는 괄호는 {}정말 후 필요하지 않습니다 then(가) 때문에 절 then이미 (와 함께 그룹화 운영자의 역할 elif, else또는 fi). 실제로 일부 쉘에서는 예를 들어 for i in 1 2 3; { echo $i; }no do또는로 쓸 수 done있습니다.
Jonathan Leffler 2016 년

1
+1 Jonathan, 저는 그 내용을 변경하고 업데이트를 업데이트했습니다
Michael Durrant

답변:


66

매번 서브 쉘을 생성하기 때문입니다.

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는 호출 할 것이다 (아무 브래킷) true또는 false명령을 사용합니다.
[ 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

선명도를 높이기 위해 공백을 과소 평가하지 마십시오.


1
오타 largest_prime=$number_under_test가 있습니다 - 그때 분기에 있어야합니다 (같은 오류가 원본에 있음)
JJoao

1
bash에서 zsh 등 [은 문자 그대로라는 프로그램을 호출하는 [반면 [[쉘에서 구현되므로 더 빠를 것입니다. 시도 time for ((i = 0; $i < 1000; i++)); do [ 1 ]; done하고 비교 하십시오 [[. 자세한 내용은 이 SO 질문 을 참조하십시오 .
kirb

2
bash [는 내장되어 있습니다. 쉘 프롬프트에서 다음을 입력 type -a [하고help [
글렌 잭맨

@glennjackman 와우; 그것을 몰랐다. 나는 which [여전히 반환 하기 때문에 여전히 그렇다고 가정했다 /usr/bin/[. 또한 zsh가 동일하다는 것을 알았습니다. 나를 위해 그것이 내장이라고 말해줍니다. 그런데 ... 왜 [[더 빠릅니까?
kirb

2
@glennjackman command -v은 또 다른 좋은 which대안입니다. 여기도 참조 하십시오 .
Abbafei

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

쉘 산술은 자체적으로 정수 조건을 평가할 수 있습니다. 너무 많은 테스트 및 / 또는 외부 과제가 거의 필요하지 않습니다. 이 while루프는 중첩 루프를 상당히 잘 복제합니다.

물론 많이 인쇄하지는 않았지만, 많이 쓰지는 않았지만, 예를 들어 위에서 언급 한대로 101 대신 천장을 16으로 설정하면 ...

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. 휴대가 쉽고 빠르며 사용하기 쉽습니다.

귀하의 서브 쉘의 문제는 대부분의 쉘에서 발생하는 것입니다, 그러나함으로써, 지금까지 A의 대부분의 급성 bash쉘. 나는 일을 번갈아 가며

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

... 그리고 천장에 101 개의 천장을 위해 여러 껍질로 위에서 썼고 dash.017 초 안에 서브 쉘없이 1.8 초 안에 서브 쉘로 수행했습니다. busybox.149 및 2, zsh .149 및 4, bash.35 및 6 및 ksh93.149 및 .160. ksh93다른 쉘은 반드시 서브 쉘을 포크하지 않습니다. 그것은이다 그래서 어쩌면 문제는 너무 많은 서브 쉘 아니다 .


[ "$((...))" -eq "$((...))" ]오버 의 장점은 무엇입니까 (( (...) == (...) ))? 후자는 이식성이 떨어 집니까?
ruakh

@ruakh-휴대 성, 속도, 신뢰성. 프로그램을 실행하는 데 15 초가 걸리지 않는[ "$((...))" -eq "$((...)) ] 쉘에서 작동 하지만 다른 것은 그렇지 않습니다. 서로의 이점이 전혀 의심 스러울 경우 전자에게만 우위를 줄 수 있기 때문에 사용할만한 이유 가 전혀 없습니다 . (( (...) == (...) ))
mikeserv

죄송하지만 귀하의 회신에 대한 쉘 지원에 대한 자세한 지식이 이미 있다고 가정합니다 (( ... )). 나는 아첨하지만 자세한 지식 없습니다. ( (( ... ))휴대용 휴대 전화 인지 물어 본 사람은 본인 임을 기억하십시오 .) 따라서 귀하의 회신을 이해하지 못합니다. :-/ 좀 더 명확하게 설명해 주시겠습니까?
ruakh

@ruakh-죄송합니다. 휴대 성이 더 좋은지 묻지 않았고, 그것이 얼마나 유리한지 보지 못했습니다. 이것이 이식성에 대해 대답 한 이유입니다. 어쨌든,이 "$((...))"입니다 POSIX 지정 하고 다른 하나는 쉘 확장 기능입니다. POSIX 쉘은 꽤 기능적입니다. 짝수 dashposh분기 테스트를 올바르게 처리 "$((if_true ? (var=10) : (var=5) ))"하고 항상 $var올바르게 할당 합니다. busybox거기에서 깨짐- $if_true의 가치에 관계없이 항상 양측을 회피 합니다.
mikeserv

@ruakh-오 남자. 나는 오늘 조금 쉬어야한다 ... 그것은 바로 거기에있다 ... 후자는 덜 휴대용 인가? 나는 전에 그것을 보지 못했다.
mikeserv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.