関数型プログラミング言語Haskell Part32
■ このスレッドは過去ログ倉庫に格納されています
関数型プログラミング言語 Haskell について語るスレです。
haskell.org (公式サイト)
https://www.haskell.org/
日本Haskellユーザーグループ
https://haskell.jp/
前スレ
関数型プログラミング言語Haskell Part31
https://mevius.5ch.net/test/read.cgi/tech/1506447188/ 手続き型でゴリゴリやってきた人にとって関数型は
マラソンの選手に、匍匐前進と水泳で近道するよう強制しているかのようです >>4
手前味噌ですが、Haskell入門以前と言う電子書籍をAmazonでご購入下さい。
まさに貴方のような方に関数脳を作ってもらうための本です^^ 初心者がHaskellでオンライン問題集みたいなの解いてんだけど
Haskellでやるとこういうの楽しくてええな CPS書き換えドリル
次の英文を○○構文を用いて同じ意味の英文に書き換えなさい的な haskell紹介してるブログとか色々あるけどどこも肝心要のところがすっぽり抜けててイライラするな・・
例えば
fact 0 = 1
fact n = n * fact(n - 1)
これ無限ループになっちゃって終わらないでしょ終端条件も書いてないし。
fact 3を展開すると
3 * ( 2 * ( 1 * ( 1 * ………
0を入力すれば1になるってことはこの先ずーっと1が繰り返されるだけだし。
同じ値が2度続いたら処理を止めるとか変な仕組みでも入ってるのかねー >>9
>無限ループになっちゃって終わらないでしょ終端条件も書いてないし。
いえいえ、終端条件が
>fact 0 = 1
です、n ∈ N たる n からはじまって 0 で終わる、と読みます
fact 3 = 3 * fact 2
= 3 * 2 * fact 1
= 3 * 2 * 1 * fact 0
= 3 * 2 * 1 * 1
で止まります. >>9
恥ずかしいぞ
>fact 0 = 1
で、これ以上再帰してないじゃん >>12
それじゃ納得できないんだけども
終わるから大丈夫仕様ですってもやっとするから
>>9で書いたように1が無限に続いちゃうと思うんだけどな
0が入力されたら1を返すってことだから
1 - 1でまた繰り返しちゃうよね
そもそもどの値が来たら終わるか全く記述がないよね
そういうところも納得できないっていうかおかしい >>13
>>11 が説明してくれたとおりなのだけど、納得できない? >>14
あの説明で納得するようならここ来てないから
あれだとブログの説明と何も変わらない
まさか1かtrueを返したら問答無用で終わる仕様だったりするのかねー >>13
ああ 言っている意味は分かったけど
続かねーぞ よく考えてみ 恥ずかしい思いするだろうけど
っていうか、>>11のひとが書いてるじゃん
fact 1 = 1 * fact 0
= 1 * 1 で終了だろ
これ納得できないとしたら、別のところで引っかかっている
パターンマッチを理解していないとか >>15
バカで思い込みが激しいって最悪だぞ?どっちかにしろ。 >>16
それじゃ終わらない
終了条件が書いていないから
なんどもいうように * 1がずーっと続くようにしか見えない
だからこの例題見たときにイライラしてたんだよね
前提となってる仕様があるけど説明省いたとかそれ系かなと >>13
では逆に質問しましょうか、そこから両者が部分的にでも合意できるポイントを探ることにしましょう
>>9
>fact 0 = 1
>fact n = n * fact(n - 1)
これをあなたはどのように解釈したかを、もう少し詳しめに記述していただくことは可能でしょうか、私の記述は今は >>13 が精一杯ですが、あなたの返答をみて改善できる点はないか検討したいと思います >>15
ほとんど同じ説明だが…
関数の定義が
fact 0 = 1
fact n = n * fact(n - 1)
となっている。
このとき、左辺が右辺に置き換えられる (簡約という)
つまり fact 3 は 3 * fact 2 に置き換えられる
そして fact 2 は 2 * fact 1 に置き換えられる
そして fact 1 は 1 * fact 0 に置き換えられる
ひとつめの関数の定義により、fact 0 は 1 に置き換えられるので
置き換えはここでとまる
以上をまとめると
fact 3
= 3 * fact 2
= 3 * 2 * fact 1
= 3 * 2 * 1 * fact 0
= 3 * 2 * 1 * 1
= 6 >>15
fact 0で問答無用で終わる仕組みですが。。。
>>11 以上にやさしい説明はないと思われ。
普通の言語(ここで >>18
終了条件は
>fact 0 = 1
です。関数 fact の引数が 0 なら、1 を関数の値として返し、fact の求値作業は終了します。
fact への入力が 1 でないならば
>fact n = n * fact(n - 1)
の定義をつかって、最初の入力 n よりも小さい値 n - 1 (< n) を使った関数定義がつかえるように変換します >>19
理解したのは>>9の通りで
仮定として1かtrueを返したら再帰は止まる仕様になってると推測してる
>>20
だからfact(1)で無限に続いちゃうよね
1 - 1で fact 0をevalしてまたfact(1)になる
そこで終了条件が必要だけど何にも書いてないから記述通りならずーっと続く
としか見えないんだよね いやだから
fact 1 で1が出てくる でまた、fact 1になって永遠に続くとかの勝手な思い込みでしょ
あるいは、
fact 0 =1 になるから、また fact 1 -1 になると思ってるとか
だから、永遠に1が続くと勘違いしちゃっている >>22
ということはやっぱり1を返したら必ず再帰は止まる仕様であってるのかな?
だったらすっきりするんだけども >>23
>1 - 1で fact 0をevalしてまたfact(1)になる
ならねーよ そこが恥ずかしいところだ 途中で。。。
まあいい。
Pythonでも再帰出来る。
特別な話じゃない。
def fact(n):
if n == 0:
return 1
else:
return (n * fact(n - 1)) >>23
>>>20
>だからfact(1)で無限に続いちゃうよね
>1 - 1で fact 0をevalしてまたfact(1)になる
>そこで終了条件が必要だけど何にも書いてないから記述通りならずーっと続く
そうはならない
fact 1 = 1 * fact 0
に簡約される
そして
fact 0 = 1 であって
fact 0 = 0 * fact(0 - 1) とは絶対にならないからここで終わる
高校数学がわかるなら、漸化式と同じだと思えばよい >>23
>1 - 1で fact 0をevalしてまたfact(1)になる
>fact 0 = 1
というのは、関数 fact のうち fact(0) (引数が 0) のときの値は 1 である、と確定的な記述をしています。ここで答えが決まったので、さらに答えを求めるための関数適用作業には入りません、それはそういう意味です
どうして eval したあと fact(1) になる、と判断したのでしょうか?
fact(0) = 1 の 1 というのは fact() の値であって、fact の引数ではなく、また fact の引数になりうる記述は 「fact 0 = 1」という記述の中には見つからないのですが
>fact n = n * fact (n - 1)
この記述のなかには、たとえば 引数が 7 だとすると「 fact 8 を fact 7 の値をつかって表現する」ということですから、fact 8 を eval する「途中で」 fact 7 を eval しようとする、ということは書かれています >>25
「1 を返したら」とまるのではなく
fact 0 = 1
と fact の求値が、この行で確定しているから、止まるのです。
fact 0 = 100
という定義であっても、とまりますよ >>28
いやいやfact(1)の評価は0なんだし
fact 0の定義があるからまた1
どう考えても続くんだよね
そもそも再起から脱出するためのコードがどこにもないから怪しいとも踏んでる
何か前提となってる仕様がありそうだ >>29
やっぱりそうなんだ
そういう重要なことは早めに説明書きしといてくれないと大混乱だわ
まースッキリしたありがと
そういう仕様なら色々めんどくさいな・・・ >>33
>いやいやfact(1)の評価は0なんだし
違う
どこにそんなことが書かれている? >>33
>いやいやfact(1)の評価は0なんだし
ここが違っています。
fact(1) の評価は 1 * fact(0) であって、 0 じゃないです
fact(0) の値が具体的になにかは、fact(0) を意識しはじめた時点では分からないのですよ >>33
おもろいな だんだんネタに思えてきた
fact nの定義をよく見ろ
fact 0 = 1 で再帰していないじゃん ここで止まるだろ 普通に考えれば
他の言語も一緒だろ ただそれをパターンマッチでやってるだけ >>34
えーと、すまん、「どんな場合でも」再帰は1を返したら終了すると考えているのなら
それは大間違いだ >>33
fact 1の評価は1 * fact 0
fact 0のみが再帰してない。(基底部)
再帰は再帰部と基底部が必ず最低1個ずつ存在する。 >>23
>>>19
>理解したのは>>9の通りで
>仮定として1かtrueを返したら再帰は止まる仕様になってると推測してる
この推測が間違っているので、いったん忘れて素直にプログラムを読んでみよう >>35
教えてもらったからもう大丈夫だよ
1で再起が止まる仕様なら納得だし
>>36
fact(1)を入力したら 1-1を評価するから fact 0
特に問題ないよ
1で止まる仕様らしいからそれを前提にコード考えないといかんのか・・
ややこしいことになるねこれ >>42
>>29 や >>31 でお墨付きを与えたのが間違いだった
君はまだ理解できていない (断言) >>39
どーゆーこと
もうそれで覚えちゃったんだけども・・ >>42
>1で再起が止まる仕様なら納得だし
理解が不十分じゃないかと危惧します、その台詞「1 で再起がとまる」という言葉ですが、「何が」 1 で再起が止まる、と考えているのですか?
あなたの発言は、いちいち「何が」「どうだ」の「何が」が欠けているので不安です >>44
覚える、という言葉はこういうときに使うものではないと思いますよ もういちど繰り返すが、
>>41 に書いたように
「仮定として1かtrueを返したら再帰は止まる仕様になってると推測している」
が大間違いだ >>45
それだと>>9の通りで揺らがない
>>46
この記述だと無限に *1が続くわけだから
なにか終了条件が必要なわけで
1を返したら再帰が止まる仕様ならなるほど納得 >>42
>fact(1)を入力したら 1-1を評価する
どうしてそう考えたのですか?そのような記述がどこに書かれていたのか説明いただけませんか? >>50
> この記述だと無限に *1が続くわけだから
何で?続かないが。ちゃんと読め。 >>48
大混乱してきた
1で再帰は必ず止まるんでないならさらにわからなくなるんだけども >>51
わかるじゃん fact 0で1が出てきたら、また、勝手に再帰させて
fact 1に戻っちゃう 勝手に脳内変換させちゃってる >>51
fact(1)はfact(n-1)によってfact(0)になるよね
実は式自体が予想外の評価するってことなのかな?
いやまさかね・・ >>50
>この記述だと無限に *1が続くわけだから
あなたの理解になにが足りないのかがわかるような気がしてきました。
「fact の引数が 1 である」と「fact の求値が 1 である」とを区別していないようですね
この二つは厳然として異なります
fact 0 = 1
は、fact(0) の「求値が」1 である、といっているのです
fact n = n * fact (n - 1)
は、たとえば
fact 8 = 8 * fact 7
fact 7 = 7 * fact 6
fact 6 = 6 * fact 5
fact 5 = 5 * fact 4
fact 4 = 4 * fact 3
fact 3 = 3 * fact 2
fact 2 = 2 * fact 1
という記述を纏めて表現しているのであり、これは fact n の「求値」は fact (n - 1) という、もともとの引数 n とは違う n - 1 という引数を使った fact の求値で定義しているのです もしかすると、だけど
fact 0 = 1
を実行した直後に
fact 1 = 1 * fact 0
が続くと考えている?
もうしそうだとしたらそこが間違いで
fact 0 = 1
fact n = n * fact(n - 1)
はそのどちらかしか実行されない >>53
>1で再帰は必ず止まるんでない
「何が」 1 で再帰が止まる、と考えているのですか?
「何が」の部分を答えてください >>54
>fact 0で1が出てきたら、
何が 1 となるのか、そこをはっきり書いてください、「出てきたら」っていいますが、「何が」 1 と出てきたら、なんでしょうか?
>また、勝手に再帰させて
何が 1 となるのか、0 となるのか、そこをはっきりさせないことには、それ以降の思考は不可能ですよ、あなたには「また勝手に再帰させて」とかいうことを考える段階ではありません >>56
ごめん意味不明わからん
>>57
もちろんそのように考えてる
fact 0 = 1の結果はすぐ反映されるんじゃないの? >>59
いや 俺は本人じゃないから
あくまでも、彼の脳内を勝手に想像しただけ 1がつづくケースを考えただけ
そのものでは無いかもしれないけど、どちらにしようが似たようなケースでしょ >>60
ああ、良かった
>fact 0 = 1の結果はすぐ反映されるんじゃないの?
そうだよ。そして fact 0 は 1 を返すから、もう再帰呼出しは起こらないよね?
fact 3
= 3 * fact 2
= 3 * 2 * fact 1
= 3 * 2 * 1 * fact 0
= 3 * 2 * 1 * 1
= 6 >>63
いやいや終了条件が何も書かれてないから結局は続くよ
どのみちfact 0が1となるならまたfact(1-1)でfact(0)でしょ
間違ってないはずなんだけど何か使ってる人には常識的なお約束事とかあるのかな >>60
>fact 0 = 1の結果はすぐ反映されるんじゃないの?
ようするにそこでとまる
数学の階乗だから fact nの引数は再帰する度に-1される。
どこかで、factの引数が0になったら、それ以上は再帰しない
難しい話じゃないし、他の言語で再帰理解していたら、分かるはず
自分で落ち着いて考えてごらん >>65
いやただのカウンターだったとしても止まらないし
やっぱりおかしいとは思う
何か前提がありそうな気はする >>64
fact 3
= 3 * fact 2
= 3 * 2 * fact 1
= 3 * 2 * 1 * fact 0 -- fact 0が・・
= 3 * 2 * 1 * 1 -- 1になる。factもうないのにここから何が続くの?
= 6 >どのみちfact 0が1となるならまたfact(1-1)でfact(0)でしょ
fact 1 = 1 * fact (1-1) = 1 * fact 0
となるけど、これは fact 1 の時の処理だね
fact 0 の場合は 1 を返すだけだから、fact(1-1) という処理はもう出てこないよ
今の話は
fact 3
= 3 * fact 2
= 3 * 2 * fact 1
= 3 * 2 * 1 * fact 0
= 3 * 2 * 1 * 1
= 6
のうち
3 * 2 * 1 * fact 0
が
3 * 2 * 1 * 1
に変化するところだよ >>66
だとすると文法自体理解していない あるいは関数自体
もっと、詳しく書くと
>どのみちfact 0が1となるならまたfact(1-1)でfact(0)でしょ
定義を見てごらん
fact 0 = 1 でこれは、=の右にfactが無いからこれで再帰はしないで、ここで終了
fact n = n * fact n-1 で=の右にfactがあるから再帰する >>67
= 3 * 2 * 1 * 1
= 3 * 2 * 1 * 1 * fact( 1 - 1 )
= 3 * 2 * 1 * 1 * 1 * fact ( 1 - 1)
どこまでも続くじゃない・・ >>70
中学校の数学でそうはならないってことは習うから、学校の数学でそこまで勉強してからプログラミングに手を出しても遅くないのでは? >>69
fact 0 = 1で終了するというのが納得いってない
もしそうであれば1を返せば再帰は終了すると仮定したんだ
そもそもfact 0 = 1ってマクロ定義みたいなもんだし
直ちに評価されて式に代入されるよね >>73
他の言語で再帰ってのは理解しているの?
fact 0 = 1で=の右にfactが無いから、再帰は終了 だから、終了条件は書かれている。
もしこれが理解できないのなら、関数定義とか、パターンマッチとかもっと基本的なことに
戻らないと理解できないと思う >>72
プログラミング言語は数学じゃないから
再帰から脱出するならそのためのコードは絶対必要なはずなんだ
それがないってことは仕掛けがあるはずで
その仮定が1を返せば再帰から脱出できるという考え
でも違うらしいから大混乱してる >>73
fact 0 = 1
で「終了する」というのが間違い
fact 0 = 1
では、fact 0 が 1を返す(正確に言うと1に簡約する)
ということしか定義していない
ただ、もうfact関数を呼び出していないので、再帰呼出しはここで止まる
そして fact 0 = 1 がマクロ定義ということも勘違いなので忘れよう
>>67 が解説してくれた
fact 3
= 3 * fact 2
= 3 * 2 * fact 1
= 3 * 2 * 1 * fact 0 -- fact 0が・・
= 3 * 2 * 1 * 1 -- 1になる。factもうないのにここから何が続くの?
= 6 >>74
Cで再帰させるなら終了条件にヒットしたらフラグたててreturnで戻ってく
関数定義の仕方ならサッと見たけど特に疑問はなかったよ
パターンマッチっていってもマクロ定義みたいなもんだし特にどうということも >>75
上に書いたけど、漸化式は理解している?
同じ書き方をします
Haskellは数学を基盤としているので、数学的な記述方法でプログラムが書けます
「再帰から脱出するならそのためのコードは絶対必要なはずなんだ」とあるけど、
もう書かれているよ
fact 0 = 1 って >>76
いやだから・・
fact 0は1なんだからその次はfact(1)になるでしょ
何も間違ってないと思うんだけど。 >fact 0は1なんだからその次はfact(1)になるでしょ
その次はもうないよ >>78
それは終了条件じゃないよね
何が正解なのかさらに混乱して来た >>81
君の言い方に合わせると「終了条件」と考えてよい
正しく説明すると
>>40 になる >>42
1で止まる仕様と言うか。。。
掛け算だから1を返すだけ(掛けても変わらない数)で、足し算なら0を返すよ。
sum1toN 0 = 0
sum1toN n = n + sum1toN (n - 1) >>81
終了条件だよ。
>>27のPythonコードで言うと
if n == 0:
return 1
の部分と同じだよ。 >>83
だから・・
fact 0は1なんだから続くじゃない?
延々と再帰するようにしか見えない この人の脳内だと、
fact 0 = 1
が
fact 0 = fact 1
になってるんじゃないだろうか >>85
PythonやJavaは知らないのでなんとも この人 77で書いてるけど、文法まともに読んでないんだよ
Haskell 文法自体は一見綺麗で簡単そうに見えるけど
実際には、理解するのが非常に難しい(今回の再帰のとこじゃないんだけど)
適当に文法流し読みして理解しようとするのが 間違っている
もっとも、階乗の再帰は、他の人の説明で理解できるだろうと思うが >>86
fact 0は1なんだから、
fact 3
= 3 * fact 2
= 3 * 2 * fact 1
= 3 * 2 * 1 * fact 0 -- fact 0が・・
= 3 * 2 * 1 * 1 -- 1になる。factもうないのにここから何が続くの?
= 6
はい!ここで一旦休憩!君に問題。
3 * 2 * 1 * 1はいくつだ? うーん、C言語だと
int fact(int n)
{
if (n == 0) return 1;
else return n * fact (n - 1);
}
と書くのと同じなんだけどねえ >>90
だから
=3 * 2 * 1 * 1 * fact(1-1)
=3 * 2 * 1 * 1 * 1*fact(1-1)
=3 * 2 * 1 * 1 * 1*1*fact(1-1)
という具合にいつまでもfactは続くよ
だから色々納得いかないことあるんだけど >>92
この1行目はどこから出てきたの?
=3 * 2 * 1 * 1 * fact(1-1)
この辺を脳内でテキトーに処理してるからわからないんだよ
fact 3
のところから地道に書きながら置き換えてみな >>94
それで地道にやったら >>9になったよ
あれ以外どうしろと・・ >>92
算数のテストで
3 * 2 * 1 * 1は?って問題に、
3 * 2 * 1 * 1 * fact(1-1)って答えるの?
それは算数でも数学でもプログラミング言語でも間違いだよ。
算数でも数学でもプログラミング言語でも答えは6だよ。 >>95
そうなのか。C言語はやったことがあるようだけど
>>91 は理解できてる? >>96
そこはどうでもいいところだから書かなかった >>95
ああ、>>9 みたいに一気に展開するのではなくて、
fact 3 =
のところからひとつずつ地道に置き換えてみた?
もしやってないなら、今すぐやってみよう >>97
あれでいいんだけど似たような制御構造がないよね
haskellのコードは全く別物に見える ■ このスレッドは過去ログ倉庫に格納されています