関数型プログラミング言語Haskell Part31©2ch.net

■ このスレッドは過去ログ倉庫に格納されています
2017/09/27(水) 02:33:08.70ID:2XAqPuH2
関数型プログラミング言語 Haskell について語るスレです。

haskell.org (公式サイト)
https://www.haskell.org/

前スレ
関数型プログラミング言語Haskell Part30
http://mevius.2ch.net/test/read.cgi/tech/1484491434/
2017/11/05(日) 08:59:13.86ID:BXN9Zljm
>>212
>対応するdo表記を調べて理解を深めたいです。

どうぞそうなさって結果をスレにてご報告ください。
2017/11/05(日) 11:02:14.73ID:33a+kqna
>>213
いろいろ実験したり、GHCのユーザーガイドを読んだりしたところ、
then group by p using f の働きが理解できました。

しかし、結局のところ対応するdo表記は分かりませんでした。
そんなものはもともと無いのかもしれません。
もしかしたらCoreを出力してみると何か分かるかもしれませんが、
自分の中ではもう問題が解決しているので、そこまで調べるモチベーションがありません。
なので、ここでその結果を報告することはできません。

代わりに、then group by p using f がどのような働きになっているのか、
私の知見だけでも述べた方が良いでしょうか。
2017/11/05(日) 13:47:42.35ID:iHS/FHvi
知見は歓迎です
2017/11/05(日) 14:29:15.46ID:g7Wivisu
https://downloads.haskell.org/~ghc/7.8.3/docs/html/users_guide/syntax-extns.html#monad-comprehensions
> D[ e | Q then group by b using f, R ] = f (\Qv -> b) D[ Qv | Q ] >>= \ys ->
> case (fmap selQv1 ys, ..., fmap selQvn ys) of
> Qv -> D[ e | R ]
> where Qv is the tuple of variables bound by Q (and used subsequently)
> selQvi is a selector mapping Qv to the ith component of Qv
別にモナドで定義されてるからdoで定義してもいいけど
Qvから要素を取り出す方法がアドホックにしか書けないから
doを使わないにしろ一対一で対応するようには書けない
2017/11/05(日) 18:08:38.19ID:Rt4/SB7E
Haskellの表記を楽にする6つのghc拡張 - Qiita
https://qiita.com/philopon/items/b812e43128654245e42d
2017/11/05(日) 23:15:17.53ID:s0bBqtdw
>>217
MultiWayIfとBinaryLiterals以外は可読性落ちとるやないかーい
2017/11/05(日) 23:28:42.70ID:33a+kqna
>>212 です。

then group by p using f の働きはいろいろ不思議な部分があるのですが、
すべての疑問は、then group by p using f で使う関数 f :: (a -> t) -> [a] -> [[a]] の第1引数は
どのような関数が渡されるのか、ということに集約されました。
(それが分かって初めて、これは then f by p の f でも同じだと気づきましたが、
こちらでは理解につまづかない程度の簡単な例しか扱っていなかったので)

この関数 f は、then group by p using f が現れるより左にある全てのバインダを要素に持つ組から p への関数
と考えれば納得がいきました。

たとえば、
[ (x, y) | x <- [1,2,1], y <- "ok", then group by x using groupWith]
ですと、then より左のバインダを要素に持つ組というのは、つまり
<1, o>, <1, k>, <2, o>, <2, k>, <1, o>, <1, k> といった組です。
(実際に内部で使われている実際のデータ型は分かりません)

このような組から x への関数なので、
g <1, o> = 1
g <1, k> = 1
g <2, o> = 2
g <2, k> = 2
という関数です。
このような関数 g が groupWith 関数の第1引数に渡されると考えると辻褄が合いました。
2017/11/13(月) 22:38:59.83ID:GpiHsSLN
haskell始めてみたいんだけどまだプログラミング能力があまりないので不安
他の言語をどれくらい書けるようにしておくべき?
2017/11/13(月) 23:15:58.10ID:IehbHsjb
むしろ手続き型言語に慣れ親しんだ脳みそだと関数型は取っ付きにくいから
いきなりHaskellでいいと思うよ
2017/11/13(月) 23:27:39.67ID:ZUv/XWe7
>>221
Masterminds of Programming という本で Haskell 委員会のひとりが、
他言語をちゃんとできるヤツはHaskellも上手く使える、という趣旨のことを言ってた。
2017/11/13(月) 23:30:10.81ID:ZUv/XWe7
>>221
それどころか、いきなりHaskellをやるのはお勧めしない感じだった。
224デフォルトの名無しさん
垢版 |
2017/11/13(月) 23:30:57.19ID:PxhTPMZi
>>222
「ちゃんと出来る」の意味が、常人と違う予感。
2017/11/13(月) 23:42:04.93ID:dMdke9KA
いきなりHaskellの難点はどのHaskell入門書籍やサイトも基本的なプログラミング知識を前提としちゃってるとこかな
変数とか関数とかリストとかマップとか
でもHaskellというか関数型パラダイムにできるだけ早い時期から触れとくのは良いことだと思う
2017/11/14(火) 00:36:12.17ID:XSI09cgL
>>220

『プログラミングの基礎』浅井健一

でOCamlやってからHaskellに逝くのが一番簡単な導入
2017/11/14(火) 00:42:09.25ID:Meoq/IF/
SICP読んでからhaskellやるのは大丈夫?
2017/11/14(火) 02:30:14.88ID:RezF4qhu
Haskellは日本語どころか
英語でググっても答えが見つからない問題にぶち当たる率が
他のメジャーな言語と比べると高すぎるから初学者向きじゃないよ

もくもく会に参加したり、Stack Overflowで質問したりと
双方向的な方法を挟まないと一定以上に到達するのは難しい
2017/11/14(火) 11:28:30.93ID:eZNGHHYs
問題自体が難しい
だから答えが見つからない
と解釈するのが自然

問題自体をよく見ようとせず、SEOや脳味噌のことを考えるのは的外れだと思う
2017/11/14(火) 19:55:37.30ID:0CBPSjnu
ocamlの元のmlで挫折しそうになった。
f#がたのしい
2017/11/14(火) 20:17:46.83ID:WUG8DCiI
例えばC言語の楽しさを1とすると
haskell,ocaml,f#の楽しさはどれぐらい?
2017/11/14(火) 21:11:59.47ID:eZNGHHYs
F#はC#の知識を前提とすることを許可されている感がある
箸の上げ下ろしどころか知識を得るのにも許可を必要とする異常な環境に適応したのがF#
2017/11/14(火) 22:22:03.84ID:0CBPSjnu
f#はなんかこう昔懐かしbasicの関数型言語版のよう
な気がする。
2017/11/14(火) 22:22:45.27ID:0CBPSjnu
haskell構文がやだ。
2017/11/14(火) 22:58:23.51ID:LvvrWEBo
>>234
たとえば、どんな構文が?

遅延評価とか参照透過性とか厳密な型付けとか、そういうのを嫌う人はいっぱいいたけど、
構文が嫌って人には初めて出会った。
2017/11/14(火) 22:59:13.88ID:wshk46M/
F#固有のライブラリ使ったことないな
構文的にC#よりF#楽
あとはHaskell並みの肩推論あれば文句ないんだけど
Haskellは遅延評価使いどころを見つけづらい
2017/11/14(火) 23:31:10.95ID:lH5WJrT0
結構凝ってるみたいっすねえ
2017/11/15(水) 01:49:26.34ID:9iJNvTMA
むしろ構文が好きだわ
限りなく不要な物は省く的な
2017/11/15(水) 07:22:37.14ID:1MOdQV57
インデントルールにたまにハマるくらいかな
一応ブレース&セミコロン方式でも書けるようだが
2017/11/15(水) 12:36:08.57ID:Lu+q1695
合成関数を左から右に書きたい
何か数学でそんな書き方もあったと思うんだけど忘れた
2017/11/15(水) 17:52:48.58ID:C1PNunQ6
Control.Arrow の (>>>) 演算子使えばいい
($) の引数入れ替えたヴァージョンなら Data.Function の (&) 演算子
2017/11/15(水) 17:55:11.80ID:C1PNunQ6
>>239
>一応ブレース&セミコロン方式でも書けるようだが

標準的なコーディングスタイルだと使わないからなあ……
コード生成のときなんかには便利だけど
2017/11/15(水) 21:31:38.92ID:ouxrorYh
Preludeに引数を入れ替えるというだけの関数なかったっけ?
マニアック過ぎて忘れてしまった
2017/11/15(水) 22:05:39.40ID:C1PNunQ6
つ flip

全然マニアックじゃないよ……
245デフォルトの名無しさん
垢版 |
2017/11/16(木) 19:05:18.93ID:U25znXQF
Haskellが競プロで得意なグラフ問題がatcoderでマラソン形式で出題されているらしい
2017/11/16(木) 20:02:11.38ID:fiTPZdVA
誰もhaskell使ってないでしょ
247デフォルトの名無しさん
垢版 |
2017/11/16(木) 20:10:09.25ID:sSGm4ReW
使うものじゃないからな。
2017/11/16(木) 20:21:11.26ID:F5ZZYHwp
競プロでHaskell使ってた人は皆Rustに移ったイメージ
2017/11/16(木) 20:28:57.17ID:BHFP68Bt
競プロは普通に手続き型かつ破壊的の方がいいもんな
入力範囲も固定だし、Haskellで挑む理由がない
2017/11/16(木) 21:32:29.59ID:F5ZZYHwp
関数型っぽく書くと実装が一瞬で完了する簡単な問題が結構あったりはする
2017/11/16(木) 21:43:12.74ID:wcx5gPy1
競プロより Project Euler で付けた力の方が将来の糧になりそう
2017/11/16(木) 22:35:40.89ID:1hTLcaK1
Haskellはアルゴリズムをうまくfold系で書ければ
融合則とかで綺麗さと速度を両立できる感じになるけど
うまく書けないとSTRefとか使うことになって
なぜHaskellやってるんだって気分になる
2017/11/17(金) 00:37:00.18ID:ehxZncBr
ストリーム処理的にしか使ってない
2017/11/17(金) 00:38:56.41ID:ehxZncBr
ストリーム処理的にしか使ってない
https://ideone.com/kYgQxV
2017/11/17(金) 11:21:31.36ID:7fJDQyWy
>>252
fold系には合わない計算があるのは分かる。

でも、なぜそれでSTRefを使うことになるのかが分からない。
2017/11/17(金) 12:39:19.61ID:edPG1sGH
別にIORefやSTRefを使ったっていいじゃない
FFIだってポインターだってあるじゃない
ただしStateモナド、テメーはダメだ
257デフォルトの名無しさん
垢版 |
2017/11/17(金) 16:36:27.24ID:FedOcmK4
よく知らないけど
カーソル的なクラスと同じように使えるんじゃない?Stateって
Stateモナドに入るのがインスタンス生成で>>=がメソッドコール
2017/11/17(金) 17:56:56.51ID:oGefn9Ej
1 s -> (a, s)
2 s -> (a -> s -> s) -> s
3 (a -> s -> s) -> s -> s
4 (a -> s -> s) -> s -> [a] -> s

1がStateで4がfoldr
2017/11/17(金) 20:49:15.19ID:M/sHB851
Haskellって他人のコード読むと

▲ a b = □ . ○ $ map ■ $ (△ a) $ ▼ $ ◎ b []
 where
  ◎ b xs = 〜長い処理〜

こんなんばかりで脳がパンクするんだが
2017/11/17(金) 20:52:24.67ID:FaJpeIXU
むしろ他人のコードでも割とサクサク読めるのがHaskellのメリットじゃね?
2017/11/17(金) 22:00:31.45ID:Tih5fc0e
>>259
せっかく参照透過性が保たれているのだから、自分が読みやすいように、
でも式の意味は変えないように変形すればいい。

複雑なコードに出会って、それでも理解したいという欲求があるのなら、
取りあえず脳がパンクしない程度の小片にまで分解してみればいいんじゃないか。
2017/11/17(金) 23:01:40.13ID:5gG4LJO3
なるほど処理を理解しきっていなくても
他人のコードを自分が読みやすく変形出来るのが参照透過性のメリットなのね。
その発想はなかったから今度から試してみる。
2017/11/18(土) 00:48:05.98ID:Na4dLuAE
>>255
foldに乗らない処理ってのが
要は愚直に再帰で書いてるループ計算で、
破壊的更新をしないとものすごいGCが走ってつらいということがあった
(データ構造にもっと工夫の余地があったとは思う)
2017/11/18(土) 02:53:34.88ID:/UaXWK/X
実際再帰って最終手段だよね。
入門者向けには基本みたいに言われている謎。
2017/11/18(土) 08:54:42.51ID:6/fTmZe2
言うほど最終手段ではないし基本なのは事実
2017/11/18(土) 12:58:42.16ID:TxAwv536
Haskellってループないんじゃなかったけ
再帰なしでどうやって組むんじゃ
2017/11/18(土) 13:13:54.77ID:qwbMNygr
>>266
リストで言うならmapとかfoldlあたりで対応できるならそれを使えばいい
ユーザー定義の再帰関数は自由度は高いけど終了条件を見落とす可能性もあるし、場合によっては末尾再帰やら正格評価やらも気にせんといかん
必要としている仕事より強く自由度が高い関数や型をあまり使わない方がいいのは他言語と同じ
2017/11/18(土) 13:17:08.17ID:6/fTmZe2
直近で書いた120行程度で関数定義25個を含んだコードを見返したが、確かに再帰は一箇所もなかった。
2017/11/18(土) 13:21:49.25ID:IKdiOjx3
要するに集合演算があるから繰り返す必要がないということか
2017/11/18(土) 13:59:18.87ID:5lmMR8NZ
再帰はいいんだけど関数定義をトップレベルに並べるとまるでgotoのように見える
しかしletは使うな、caseは使うなという人もいるから
消去法で高階関数かな
2017/11/18(土) 17:01:09.59ID:Sb0VMRtj
再帰と言えば、今 haskell.org のサイトのトップにある prime の定義って素敵だよね。
こういう宣言的で自己説明的なプログラムが普段のコードでも書けるように精進したい。
2017/11/18(土) 19:18:50.08ID:+f9/gyau
以下で、なぜエラーが出るのかがわからないです。

import Data.Typeable

f :: (Show a, Typeable a) => a -> String
f a = if typeOf a == typeRep (Proxy :: Proxy String) then a else show a

以下のようにすると、なぜかうまくいきます。

f a = if typeOf a == typeRep (Proxy :: Proxy String) then read $ show a else show a
2017/11/18(土) 20:12:39.81ID:Sb0VMRtj
>>272
前者で then a else show a としているから、a は String 型でしかありえない。
一方、型シグネチャにより、a は String 型以外の型も認めている。
矛盾している。

試しに前者を、型シグネチャを書かずに関数定義だけ書いて、ghci の :t で型を調べてみよう。


次回から、エラーの内容も書こう。
2017/11/19(日) 00:17:21.62ID:K9yRHYlu
そこにエラーを出してくれるのがHaskellの良い所
動かす前に叱ってくれるって素晴らしい
2017/11/19(日) 07:14:38.53ID:gzKE6jPG
>>273
ありがとうございます
2017/11/19(日) 14:34:38.43ID:Sx00JERW
>>271
数学的な定義をそのまま書いて動くっていうのは確かにかっこいいところ
その代わりあの定義では正しい篩になってなくて割と遅かったはず
2017/11/19(日) 17:33:53.76ID:FtsW9SXJ
ちゃんと書くとこんな感じになる

primes = 2:(section [3..] composites)
 where
  composites = union [multiples p |p<-primes]
  multiples n = map (n*) [n..]

section (x:xs) (y:ys)
 | x < y = x : (section xs (y:ys))
 | x == y = section xs ys
 | x > y = section (x:xs) ys

union = foldr merge []
 where
  merge (x:xs) ys = x:merge' xs ys
  merge' (x:xs) (y:ys)
   | x < y = x : merge' xs (y:ys)
   | x == y = x : merge' xs ys
   | x > y = y : merge' (x:xs) ys
2017/11/19(日) 20:38:10.15ID:21tWIVgE
>>277
かっこいいコードだけど
省ける計算が多い分まだ試し割の方が速いんだな
primes = 2 : filter isPrime [3..]
isPrime x = all ((/=0) . (x`mod`)) $ takeWhile ((<=x) . (^2)) primes
数が大きくなればmodのコストが問題になるのかもしれないけど
2017/11/19(日) 20:53:42.98ID:CsMCJwUW
>>278
かっこいいの? どの辺りが魅力的なん?
2017/11/19(日) 20:54:52.08ID:FtsW9SXJ
一応言っとくと >>277 はRichard Bird がよくあるヴァージョンを批判されてきちんと篩を書いたコード
2017/11/19(日) 21:08:29.23ID:21tWIVgE
>>279
modを使わない所がお洒落
2017/11/19(日) 21:13:45.49ID:aWoSpo3i
でも解りにくいじゃん!
Haskell知らなくても何をしてるか
勝手に分かってしまうコードが宣伝用には理想

そういう視点でも
メモ化フィボナッチコードは神コードだね
2017/11/19(日) 22:01:10.75ID:3wAGigTa
フィボナッチってどこで使う場面があるのさ?競プロ?
2017/11/19(日) 22:04:47.47ID:CsMCJwUW
>>282
それは >>276 が「その代わり」ってちゃんと断ってるじゃん。
たいていの場合、両立なんてできないんだよ。

メモ化フィボナッチが希有な例なんだよ。


>>283
全くない。
だから、学ぶべきはフィボナッチ数列の作り方じゃなく、
宣言性を壊さないままメモ化する技術の方なんだが、
あの本にはその辺りのことは書かれていなかったような気がする。
メモ化フィボナッチは単なる例にとどまってたんじゃなかったか。
2017/11/19(日) 22:31:12.52ID:FtsW9SXJ
「あの本」?
2017/11/20(月) 09:32:22.51ID:2YC1SjJx
f1 = (map f1' [0..] !!)
 where
  f1' 0 = a
  f1' n =〜
の形は

f2 n = foldl' f2' a [1..n]
 where
  f2' n =〜

より分かりやすい。
import Data.List (foldl')という
余計なインポートが不要なのが最高。
2017/11/20(月) 10:30:22.80ID:ybEAOaLd
ghcコマンドやghciコマンドの出力メッセージの中で、
引用符が倍角文字になってるんだけど、これ半角に設定できないかな。

ターミナルで使ってるフォントの関係でかなり読みにくい。
2017/11/20(月) 19:19:15.77ID:i6dwbdTS
わかりやすいもなにも map で畳み込み計算をどうやって書くつもりなんだろ
できなくはないけど余再帰使うはずで、そうなればもう map とかそういう問題ではない

というかscanlをmapで再実装するのがそんな最高なのかしら……
2017/11/20(月) 23:56:42.78ID:POIf14n/
foo (x:xs) = 〜(なんか関数)〜 (x:xs)

foo a@(x:xs) = 〜(なんか関数)〜 a

したとき、前者と後者は内部的に処理違ったりする?
2017/11/21(火) 08:36:28.75ID:jCVIG8Yh
>>289
GHCの処理方法が分からないなかで、
それでも違うかどうかを調べるには、たとえば次の方法がある。

・Coreを比較する
・プロファイルを比較する
・バイナリを比較する
2017/11/21(火) 11:49:36.85ID:Zx2Txvw4
>>288
普通ならリストが空か否かの場合分けで2行ぐらい書くところを
余再帰なら1行で書けるんだろ
2017/11/21(火) 14:03:16.67
>>224
C++ちゃんとできる


(ガサガサッ

(ビュンッ!
2017/11/21(火) 17:45:32.32ID:Ftr24wOW
C++ちゃんってあれだろ?
「ブーストかけるぜ」なCちゃんの別人格

Haskellちゃんも見たかったなぁ…
2017/11/21(火) 19:32:07.35ID:Egb8Rv0R
>>289
ghci -ddump-simpl で起動して、問題の関数定義を let 束縛してみたら
Coreのレベルで完全に同じだとわかる

まあ、そりゃそうだろって感じだが
2017/11/21(火) 20:06:56.72ID:NQH8f0G2
バイナリでは違いが出たよ
2017/11/22(水) 00:50:08.76ID:ovFpIkAx
コンパイル時刻
297デフォルトの名無しさん
垢版 |
2017/11/22(水) 09:20:37.07ID:CArcd2ol
f (x:xs) = (x:xs) → case 引数 of _ -> x : xs
g a@(x:xs) = a →
だと
2017/11/22(水) 09:28:04.15ID:CArcd2ol
ミスった
ghci -ddump-simplだと
f (x:xs) = (x:xs) → case 引数 of _ { (x:xs) -> x:xs }
g a@(x:xs) = a → case 引数 of a { (x:xs) -> a }
みたいなのが出力されたけど・・・
2017/11/22(水) 10:01:50.65ID:KMROYMtU
やってみた

https://ideone.com/6r6LpA

@パターンは不可反駁なんで、少しだけ違うね
9行目と31行目で a は不可反駁なんでワイルド扱い
15行目と37行目もまさに同じ点が違う
2017/11/22(水) 10:27:32.10ID:DL3EaaUj
stackoverflowで聞けば識者が的確に答えてくれそう
2017/11/22(水) 18:10:15.64ID:ovFpIkAx
最適化で消えるだろう
2017/11/23(木) 04:31:10.10
《問》(配点 10点)
\n -> 1 + n
\f n -> f 1 n
\f -> f 1 . (\g n -> g 2 n)

らはスーパーコンビネータである。しかし、

\f g -> f 1 . (\n -> g 2 n)

はスーパーコンビネータではない。何故か?

《ぼくの解答》
g がラムダ抽象のスコープ外で束縛されており、そのスコープにおいて自由変数である。
従ってこのラムダ抽象は閉じた項ではないのでコンビネータではない。
コンビネータでも定数でもないのでこのラムダ抽象はスーパーコンビネータではない。
全体としては、定数でなく、されどコンビネータではあるが、その内部にスーパーコンビネータでないものを含んでいるのでスーパーコンビネータではない。



何点貰えますか?
2017/11/23(木) 04:34:25.61
訂正(一行目)
× g がラムダ抽象の〜
○ g が括弧内のラムダ抽象の〜
2017/11/23(木) 06:11:27.29ID:S1VTy0fD
(->) は?
2017/11/23(木) 09:12:04.55ID:GxHnNEoE
>>302

¥n1 n2 … -> E

がスーパーコンビネータであるのは

(1)Eに出現する自由変数が n1〜n2 だけであって、かつ、
(2)Eに現れるラムダ抽象がスーパーコンビネータであるとき、

そしてそのときに限る。

¥f g -> f 1 . (¥n -> g 2 n)

の場合、 ¥n -> g 2 n は g が(1)を満たさないのでスーパーコンビネータではなく、
したがって(2)によって、それを含む全体がスーパーコンビネータではないこよになる。

スーパーコンビネータの定義ないし必要十分条件を示してないと減点するのが普通。
2017/11/23(木) 09:13:02.72ID:GxHnNEoE
表記ちょっとミスったけどまあ分かるでしょ
2017/11/23(木) 09:16:33.82ID:GxHnNEoE
ああ……

(2)Eに現れる「すべての」ラムダ抽象がスーパーコンビネータであるとき、

だ。肝腎のとこが抜けてる……寝よう……
2017/11/25(土) 23:17:22.88
>>305
Haskell High Performance Programming には、
A supercombinator is either a constant, say 1.5 or ['a'..'z'], or a combinator whose subexpressions are supercombinators.
と説明されてますが、これは嘘なのですか?
2017/11/26(日) 00:11:02.93ID:6YUjT6hC
>>308
それだと

¥x y -> (x + y)

ですらsupercombinatorにならんよ
2017/11/26(日) 01:59:13.32
むむむ…
(x+y)に現れる全てのラムダ抽象とはなんですか?
2017/11/26(日) 07:38:30.34ID:6YUjT6hC
>>310
この場合、(x+y)にはラムダ抽象が現れていないので条件(2)は空虚に充足されている
あとは条件(1)も充足されてるから、スーパーコンビネータになる
2017/11/26(日) 12:17:18.61ID:qOXUNg+f
>>308
subexpressionの定義が曖昧だな
例えば \ y -> x は \ x y -> x のsubexpressionか?
■ このスレッドは過去ログ倉庫に格納されています
5ちゃんねるの広告が気に入らない場合は、こちらをクリックしてください。