お遊び記事です.こちらの記事の姉妹版です.いやその,某ラノベ読んでたらとりあえず21番目までのフィボナッチ数列を求める魔術式ワンライナーが書きたくなりましてね,はい.
※なお,ここでいう『ワンライナー(one-liner program)』はシェル芸のようなものとお考えいただけますと幸いです.高い可読性を要する開発現場で混ぜるな危険.
【2021-06-20追加】姉妹版『素数一覧』の続編記事を立ち上げました.こちらと同じスタイルで編集し直しています.
【2021-06-18追加】集まった記述例(2021-06-17更新時点)を動画にしてみました.
制約
フィボナッチ数を求める方法はいろいろありすぎるのと,ワンライナーの趣旨に沿うため,次の制約を設けます.基本的に,REPLを用いて0~21番目の値(0番目は0,1番目は1)を求めるワンライナー実行を想定しています.
- 一般項による計算は不可
- フィボナッチ数(列)を求める標準関数・手続き等は使用不可
- ライブラリやモジュールの追加読み込み不可
こうすると,自ずと関数型プログラミングに近くなるかもしれません.ますます魔(略 気のせいでした.
なお,次は可としています.
- REPL起動時に自動的に読み込まれるライブラリやモジュール
- 大域変数代入の複文(セミコロン区切り等)
- 上限値による処理終了判定(値が10946を超えるまで求める等)
各言語での記述例
最も短いと思われる記述(+筆者が直接確認した実装・バージョン)のみを載せています.より短い表現&他言語版歓迎.
2&(0 1,+/\)~21
|^2,*+*...*>1e4
{⍵,+/¯2↑⍵}⍣20⊢0 1
1 0[pdsarla+d10947>m]dsmx
([a=-b=1]*22).map{b=a+a=b}
for(b=1;b+=a<28658;a=b-a)a
say$@=($b+=$@)-$@for++$@..22
let f=0:scanl(+)1f in take 22f
for(b=1;b<2e4;a=(b+=a)-a)print a
1|>(a,b=0)->0:21 .|>_->a=(b+=a)-a
[0 1 20{dup 2 index add}repeat]==
a=-1;b=1;[b:=a+(a:=b)for _ in'_'*22]
: f 1 0 22 0 do dup . tuck + loop ; f
BEGIN{for(b=1;b<2e4;b=a+(a=b))print+a}
A=0:B=1:FORI=0TO21:?A:B=A+B:A=B-A:NEXT
$x=0;$y=1;0..21|%{$x;$x,$y=$y,($x+$y)}
c(a<-0,b<-1,replicate(20,b<<-a+(a<<-b)))
[a=-1,b=1,...Array(20)].map(c=>b=a+(a=b))
a:=-1.b:=1.(0to:21)collect:[:n|b:=a+(a:=b)]
a=0;b=1;for i=0,21 do print(a);a,b=b,a+b end
(take((rec(? a b)(lcons a(?(+ a b)a)))0 1)22)
$a=0;for($b=1;$b<2e4;$a=($b+=$a)-$a)echo"$a ";
reduce(lambda x,_:x+[x[-1]+x[-2]],'_'*20,[0,1])
g=lambda*a:len(a)<22and g(*a,a[-1]+a[-2])or a;g(0,1)
(take 22((fn f[](lazy-cat[0 1](map +(f)(rest(f)))))))
(do((a 0 b)(b 1(+ a b))(l '()`(,@l ,a)))((> b 2e4)l))
for(int a=0,b=1;b<1e4;System.out.println(b=a-b))a+=b;
var a=0;var b=1;while(b<1e4){println(a);b=a+b;a=b-a;}
a=0;b=1;for i in {0..21};do echo $a;t=$((a+b));a=$b;b=$t;done
fun f(a:Int,b:Int):Int=if(a<1e4){println(a);f(b,a+b)}else a;f(0,1)
MAKE "A 0 MAKE "B 1 REPEAT 22[PRINT :A MAKE "B :A+:B MAKE "A :B-:A]
Enum.reduce(1..20,[0,1],&(&2++[&1-&1+Enum.at(&2,-2)+List.last(&2)]))
(let((a 0)(b 1))(while(< b 2e4)(princ a)(princ" ")(setq b(+ a b)a(- b a))))
WITH t(a,b)AS(SELECT 0,1 UNION SELECT b,a+b FROM t)SELECT a FROM t LIMIT 22;
lists:reverse(lists:foldl(fun(_,A)->[hd(A)+hd(tl(A))|A] end,[1,0],lists:seq(1,20))).
a=0;b=1;while true;do t=$((a+b));echo $a;case $a in(10946)break;;esac;a=$b;b=$t;done
echo {g->g(g)}({g->{n,r->n<0?r :g(g)(n-1,[{g->g(g)}({g->{n,a,b->n==0?a :g(g)(n-1,b,a+b)}})(n,0,1)]+r)}})(21,[])
=LAMBDA(g,g(g))(LAMBDA(g,(LAMBDA(n,r,IF(n<0,r,g(g)(n-1,LAMBDA(g,g(g))(LAMBDA(g,LAMBDA(n,a,b,IF(n=0,a,g(g)(n-1,b,a+b)))))(n,0,1)&" "&r))))))(21,"")
各言語での記述例(番外編)
筆者が実行未確認だったり,ワンライナーと呼ぶには微妙な実行方法(シェル経由やGUI操作,基本ライブラリ追加読込等)だったりするものです.進捗(?)によっては本来の記述例に昇格するかも.
8000 AF 67 6F 11 01 00 F9 E5 19 EB 82 30 FA
echo '.globl main;main:mov $0,%ax;mov $1,%dx;lea f(%rip),%rdi;mov $22,%ecx;l:mov %ax,(%rdi);add $2,%rdi;add %dx,%ax;xchg %dx,%ax;loop l;mov $0,%eax;ret;.bss;f:.zero 44'|gcc -x assembler -&&./a.out
echo 'a;main(b){for(;b<1e4;printf("%d ",b=a-b))a+=b;}'|gcc -w -x c -&&./a.out
printf "#include<iostream>\\nint main(){for(int a=0,b=1;b<1e4;std::cout<<(b=a-b)<<' ')a+=b;}"|g++ -x c++ -&&./a.out
printf '#include<cstdio>\nint a,b=1;int main(){for(;b<1e4;printf("%%d ",b=a-b))a+=b;}'|g++ -x c++ -&&./a.out
echo ' a=0;b=1;doi=0,21;print*,a;b=a+b;a=b-a;enddo;end'|f77 -x f77 -&&./a.out
as <<< '.altmacro;.macro p n;.print "\n";.endm;a=0;b=1;.rept 22;p%a;b=a+b;a=b-a;.endr'
echo 'assert(f(0,A,_,A)). assert((f(N,A,B,R):-X is N-1,Y is A+B,f(X,B,Y,R))). assert(p:-forall(between(0,21,X),(f(X,0,1,R),format(R),nl))). p,halt.'|swipl -q
a,b:=0,1;for i:=0;i<22;i++{fmt.Println(a);a,b=b,a+b}
echo 'fn main(){let mut a=0;let mut b=1;while a<10947{println!("{}",a);b=a+b;a=b-a;}}'|rustc -&&./rust_out
備考
記事に関する補足
- ~~reduceつおい.~~次々と反復構文に置き換えられてしまった….
- Jの例はねっとからのぱくりです.独自には記述できなかった….→そしてそのぱくりから更に短く(コメントより).Jにはどうあがいても短さでは勝てなさそう.→Rakuが勝った.→Jが追い上げた
更新履歴
- 2021-07-25:Jの記述例を変更(コメントより)
- 2021-07-20:Jの記述例を変更(コメントより)
- 2021-07-18:Jの記述例を変更(コメントより)
- 2021-06-26:Kotlinの記述例を変更(コメントより)
- 2021-06-25:Excel(ベータ版)の記述例を追加
- 2021-06-23:Python3.8の記述例を変更(コメントより)
- 2021-06-21:Perlの記述例を変更(コメントより)
- 2021-06-21:冒頭に『素数一覧』続編記事について記載
- 2021-06-20:Perlの記述例・実行方法を変更(コメントより)
- 2021-06-20:C++の記述例(番外編)を変更・追加(コメントより)
- 2021-06-20:Go言語,Rust,C++の記述例(番外編)を追加
- 2021-06-19:GNU assemblerの記述例(番外編)を変更(コメントより)
- 2021-06-19:GNU assemblerの記述例(番外編)を追加(コメントより)
- 2021-06-19:PHP,bc,calcの記述例を変更(コメントより)
- 2021-06-19:Vim,bc,calc,APL,Elixirの記述例を追加(コメント等より)
- 2021-06-18:制約欄を更に整理・書き直し
- 2021-06-18:Smalltalkの記述例を修正(Twitterより)
- 2021-06-18:Haskellの記述例を変更(Twitterより)
- 2021-06-18:冒頭にYouTubeに公開した記述例の動画(2021-06-17更新時点)を追加
- 2021-06-18:Kotlin,dc,PowerShell,Emacs Lisp,bash,PHP,Scala,LOGOの記述例を追加(コメント等より)
- 2021-06-17:Fortranの記述例(番外編)を追加(コメントより)
- 2021-06-17:制約欄を整理・書き直し
- 2021-06-17:冒頭にワンライナーに関する注意書きを追加
- 2021-06-17:Z80の記述例(番外編)を変更(コメントより)
- 2021-06-17:制約欄に『0~21番目の値(0番目は0,1番目は1)を求める』を明記
- 2021-06-17:IchigoJamBASICの記述例を番外編から移動(コメントより)
- 2021-06-17:Cの記述例(番外編)を変更(コメントより)
- 2021-06-17:awk,Haskell,PostScriptの記述例を変更(コメント,Twitterより)
- 2021-06-17:Java,Scheme(Gauche限定),sh(dash)の記述例を追加
- 2021-06-16:Cの記述例(番外編)を変更(コメントより)
- 2021-06-16:各言語での記述例(番外編)の欄を追加(Z80,x86-64,IchigoJamBASIC,C,Prolog)
- 2021-06-16:下記制約欄記載に基づきScheme/Common Lisp,awkの記述例を変更
- 2021-06-16:制約欄に『21番目の値を求める』は『値が10946を超えるまで求める』も可の旨記載
- 2021-06-16:記述例について,最新版での記述の長さ順に整列
- 2021-06-16:制約欄にREPLを用いたワンライナー実行の想定,副作用OKの旨記載
- 2021-06-16:JavaScript,Perl,Smalltalk,Python2,Python3,Rubyの記述例を変更(コメントより)
- 2021-06-16:Forth,R,SQL,Raku,Erlang,awk,Luaの記述例を追加(Twitter,コメント等より)
- 2021-06-16:確認した実装・バージョンとREPL実行の旨記載
- 2021-06-15:Ruby,Jの記述例を変更(Twitter,コメントより)
- 2021-06-15:SchemeとCommon Lispの記述例を変更・統合(Twitterより)
- 2021-06-15:Haskell,PostScript,Smalltalk,Perl,Clojure,Julia,Python3.8の記述例を追加(Twitter,コメントより)
- 2021-06-15:初版公開(Scheme,Common Lisp,Python2,Python3,Ruby,JavaScript,J)