코드 분석 섹션에서 코드를 분석합니다 . 그 전에 나는 보너스 자료의 몇 가지 재미있는 섹션을 제시합니다.
하나의 라이너 하나의 문자 1
say e; # 2.718281828459045
위의 링크를 클릭하면 Damian Conway의 e
Raku 컴퓨팅 관련 특별 기사를 볼 수 있습니다.
이 기사는 많은 재미가 있습니다 (결국 데미안입니다). 컴퓨팅에 대한 매우 이해하기 쉬운 토론입니다 e
. 그리고 Larry Wall이 후원 한 TIMTOWTDI 철학에 대한 Raku의 중탄산염 환생에 경의를 표합니다. 삼
애피타이저로서 기사의 중간 쯤에서 인용 한 내용은 다음과 같습니다.
이러한 효율적인 방법은 모두 무한한 일련의 항을 합산하여 동일한 방식으로 작동한다는 점을 감안할 때 우리에게 그렇게하는 기능이 있다면 더 좋을 것입니다. 그리고 함수가 자체적으로 정확한 답을 얻기 위해 실제로 포함해야 할 시리즈의 초기 서브 세트의 양을 계산할 수 있다면 더 좋을 것입니다. 그것을 발견하기 위해 여러 번의 시도.
그리고 Raku에서와 마찬가지로, 우리가 필요로하는 것을 구축하는 것은 놀랍도록 쉽습니다.
sub Σ (Unary $block --> Numeric) {
(0..∞).map($block).produce(&[+]).&converge
}
코드 분석
시리즈를 생성하는 첫 번째 라인은 다음과 같습니다.
my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
클로저 ( { code goes here }
)는 항을 계산합니다. 클로저에는 암시 적 또는 명시적인 서명이 있으며,이를 통해 인수의 수를 결정합니다. 이 경우 명시적인 서명이 없습니다. 의 사용 $_
( 은 "주제"변수 에 바인딩 된 하나 개의 인수가 필요합니다 암시 서명의) 결과 $_
.
시퀀스 연산자 ( ...
)를 반복하는, 폐쇄의 인수로 이전 용어를 통과, 그 왼쪽의 폐쇄를 호출 게으르게 이 경우에 오른쪽에 엔드 포인트까지 용어의 시리즈를 구축 *
, 속기에 대한 Inf
일명 무한대.
클로저에 대한 첫 번째 호출 주제는 1
입니다. 따라서 클로저 1 / (1 * 1)
는 시리즈의 처음 두 항을로 계산하여 반환합니다 1, 1/1
.
의 주제 번째 호출이 이전의 값, 1/1
즉, 1
다시. 따라서 클로저는 계산하고 반환 1 / (1 * 2)
하여 시리즈를 확장합니다 1, 1/1, 1/2
. 모두 좋아 보인다.
다음 폐쇄로 계산 1 / (1/2 * 3)
이다 0.666667
. 그 용어는이어야합니다 1 / (1 * 2 * 3)
. 죄송합니다.
코드가 수식과 일치하도록 만들기
귀하의 코드는 공식과 일치해야합니다.
이 수식에서 각 항은 계열에서의 위치 를 기반으로 계산됩니다 . 일련 의 k 번째 항 (여기서 k = 0 인 경우 1
)은 계승 k 의 역수입니다.
(따라서 이전 기간 의 가치 와는 아무런 관련이 없습니다 . 따라서 이전 기간 $_
의 가치 를받는을 폐쇄에 사용해서는 안됩니다.)
계승 접미사 연산자를 만들어 봅시다 :
sub postfix:<!> (\k) { [×] 1 .. k }
(접근 ×
곱셈 연산자로, 일반적인 ASCII 접두사의 유니 코드 별칭 이 더 *
좋습니다.)
그것은 속기입니다 :
sub postfix:<!> (\k) { 1 × 2 × 3 × .... × k }
(중괄호 안에 의사 메타 신택 틱 표기법을 사용하여 필요한만큼 많은 단어를 더하거나 빼는 아이디어를 표시했습니다.
보다 일반적으로, op
식 시작 부분에 대괄호 안에 대입 연산자 를 넣으면에 해당하는 접두사 연산자가 형성 reduce with => &[op],
됩니다. 자세한 내용은 축소 메타 오퍼레이터 를 참조하십시오.
이제 새로운 factorial postfix 연산자를 사용하여 클로저를 다시 작성할 수 있습니다.
my @e = 1, { state $a=1; 1 / $a++! } ... *;
빙고. 이것은 올바른 시리즈를 생성합니다.
... 그렇지 않을 때까지 다른 이유로. 다음 문제는 숫자 정확도입니다. 그러나 다음 섹션에서 다루겠습니다.
코드에서 파생 된 하나의 라이너
아마도 세 줄을 하나로 압축하십시오.
say [+] .[^10] given 1, { 1 / [×] 1 .. ++$ } ... Inf
.[^10]
에 의해 설정된 주제에 적용됩니다 given
. (의 ^10
줄임말로 0..9
, 위의 코드는 시리즈에서 처음 10 개의 항의 합을 계산합니다.)
나는 $a
다음 학기에서 폐쇄 계산을 제거했습니다 . 고독 $
은 (state $)
익명 상태 스칼라 와 동일 합니다. 을 (를) 초기화 $a
하여 수행 한 것과 동일한 효과를 얻기 위해 사후 증분 대신 사전 증분으로 만들었습니다 1
.
우리는 이제 아래의 의견에서 당신이 지적한 최종 (큰!) 문제가 남았습니다.
피연산자 중 어느 것도 Num
(float이므로 근사값) 인 경우, /
연산자는 일반적으로 100 % 정확함 Rat
(제한된 정밀도 합리적)을 반환합니다 . 그러나 결과의 분모가 64 비트를 초과하면 그 결과는 Num
정확도로 성능을 교환하고 싶지 않은 트레이드 오프 로 변환됩니다 . 우리는 그것을 고려해야합니다.
무제한 정밀도 와 100 % 정확도 를 지정하려면 FatRat
s 를 사용하도록 연산을 강제 실행하십시오 . 제대로 이렇게하려면, 단지하게 (적어도) 피연산자 중 하나가 될 FatRat
(그리고 아무도 다른 사람은 수 Num
) :
say [+] .[^500] given 1, { 1.FatRat / [×] 1 .. ++$ } ... Inf
이것을 십진수 500으로 확인했습니다. Raku 언어 또는 Rakudo 컴파일러의 한계를 초과하여 프로그램이 충돌 할 때까지 정확하게 유지 될 것으로 기대합니다. ( 내가 그것에 대한 토론을 위해 65536 비트 너비의 bigint를 원시 정수로 개봉 할 수 없다는 대답을 참조하십시오 .)
각주
1 라쿠 포함, 내장 된 몇 가지 중요한 수학적 상수가 e
, i
그리고 pi
(그 별명 π
). 따라서 수학 책과 비슷하게 라쿠 어로 오일러의 정체성을 쓸 수 있습니다. 오일러의 정체성에 대한 RosettaCode의 Raku 항목에 대한 크레딧으로 :
# There's an invisible character between <> and iπ character pairs!
sub infix:<> (\left, \right) is tighter(&infix:<**>) { left * right };
# Raku doesn't have built in symbolic math so use approximate equal
say e**iπ + 1 ≅ 0; # True
2 Damian의 기사는 반드시 읽어야합니다. 그러나 'raku "euler 's number" 에 대한 Google 의 100 개 이상의 경기 중 하나 인 훌륭한 치료법 중 하나 일뿐 입니다.
3 python 팬이 작성한 TIMTOWTDI의보다 균형 잡힌 견해 중 하나는 TIMTOWTDI와 TSBO-APOO-OWTDI 를 참조하십시오 . 그러나 TIMTOWTDI를 너무 멀리 가져 가면 단점 이 있습니다 . 이 후자의 "위험"을 반영하기 위해 Perl 커뮤니티는 유머러스하게 길고 읽을 수없고 절제된 TIMTOWTDIBSCINABTE를 만들어 냈습니다. 한 가지 이상의 방법이 있지만 때로는 일관성이 나쁜 것은 아닙니다. 이상하게도 Larry는 중탄산염을 Raku 디자인에 적용했고 Damian은이를 Raku의 컴퓨팅 e
에 적용했습니다 .