関数型プログラミング言語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/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か?
2017/11/26(日) 13:55:44.55ID:VpgSp9ND
純粋関数型どころか関数型言語のTDD本が無いのですが、
Java用に書かれた本に載っている方法論のどは、
Haskell で TDD を行う際にも取り入れられる事は多いですか?

例えば最近出た「テスト駆動開発」(Kent Beck著)など。
最初の方をチラ読みしたところ、大変読みやい印象を受けたので、
参考になればいいなと思いましたが、どうでしょうか。
2017/11/26(日) 23:25:24.41ID:k9PNaQXG
次の型があるとします。

data T = D1 {d1a :: Int, d1b :: String} | D2 {d2a :: Char, d2b :: Double, d2c :: Integer}

同じデータコンストラクタ由来の値同士の比較関数を次のように作るとします。

smallerThan :: T -> T -> Bool
smallerThan t1@(D1 _ _) t2@(D1 _ _) = d1b t1 <= d1b t2
smallerThan t1@(D2 _ _ _) t2@(D2 _ _ _) = (d2b t1, d2c t1) <= (d2b t2, d2c t2)
smallerThan _ _ = undefined

これを、D1 や D2 の定義内のフィールドの順を変えたり、フィールドの数が変わったりしても、
smallerThan 関数の定義は修正しなくても良いようにしたいのですが、方法はあるでしょうか。
要するに、パターンマッチにおいてデータコンストラクタ名のみマッチングテストをしたいのですが・・・
2017/11/27(月) 00:32:04.34ID:4PKE/tcf
>>314
言語拡張 PatternSynonyms,RecordWildCards を使うといけるようだよ。

pattern D1_ <- D1 {..}
pattern D2_ <- D2 {..}

として、後は t1@D1_ t2@D1_ のようにパターンマッチして利用する。
2017/11/27(月) 00:44:20.26ID:ZVUfUGVW
ていうか、 拡張使わなくても空レコードでパターンマッチできたはず

smallerThan (D1{}) (D1{})

みたいに。
2017/11/27(月) 00:51:37.87ID:ZVUfUGVW
ほれ
https://paiza.io/projects/EKoIGfBN48OP_bZZW2qlug
2017/11/27(月) 07:38:07.20ID:ysH8mXge
なるほど、空レコードでマッチングできる事を知りませんでした。
ありがとうございました。
2017/12/02(土) 10:21:44.75ID:SLnkjfbB
ある名前の型がどのモジュールで定義されているのか、
あるいはまだ定義されていないのかを hoogle で調べる事は出来るでしょうか?
2017/12/02(土) 10:53:38.35ID:pAxOhb0h
実際に hoogle つかってみりゃいいじゃないか
2017/12/02(土) 12:27:16.11ID:SLnkjfbB
>>320
すみません、かなり恥ずかしい勘違いをしていました。
できました。
2017/12/06(水) 00:26:39.18ID:4CiAHXyH
tanakhにはもっとhaskellの話をしてほしいよ
2017/12/06(水) 00:47:59.63ID:IlK1069D
haskellよりrust
2017/12/06(水) 05:28:21.98
tanakhには今だけはPEZYの話聞きたい
2017/12/07(木) 18:59:33.65ID:HvbKuO/f
GHCで、モナドの各インスタンスの(>>=)関数などの、型ではなく実装を見たい場合はどこを見ればいいのでしょうか
特にIOモナドが気になってます
2017/12/07(木) 20:09:35.72ID:RO0W0ydd
https://hackage.haskell.org/package/base-4.10.1.0/docs/System-IO.html#t:IO

IO の instances の中から Monad IO 選んで各演算子の source 見ればいい。
2017/12/07(木) 20:10:53.27ID:RO0W0ydd
https://hackage.haskell.org/package/base-4.10.1.0/docs/src/GHC.Base.html

要するにこれね。
2017/12/07(木) 20:30:31.60ID:g7/WaViS
>>326
>>327
おお、ありがとうございます
重ね重ね申し訳ないのですが、これによるとIOモナドの(>>=)はbindIOでbindIOは
bindIO :: IO a -> (a -> IO b) -> IO b
bindIO (IO m) k = IO (\ s -> case m s of (# new_s, a #) -> unIO (k a) new_s)
となっているのですが、ここでcase式を使っているのはどういった意図があるのでしょうか
letと同じ使い方ですか?
2017/12/08(金) 00:03:17.81ID:Xz8G+NtW
caseは先に式を評価してからパターンに含まれる変数を束縛する

でもletには再帰があるかもしれないから先に変数束縛する
その変数を含むかもしれない式は遅延評価する
2017/12/08(金) 00:12:07.08ID:Hb+0BqaU
>>329
なるほど、そうだったんですね
勉強になりました、ありがとうございます
2017/12/09(土) 22:36:37.66ID:rGCS9XaY
Stack の LTS 9.17 に repa って無いのな
2017/12/10(日) 00:28:18.35ID:61nYDsc2
rapeならあるかも
333デフォルトの名無しさん
垢版 |
2017/12/13(水) 13:00:04.55ID:BNG1q+wP
stackでパッケージをインストールする時にコンパイルしなくなったって話を聞いたけど本当ですか?
バイナリをダウンロードしてインストールするみたいな?
化石スペックの俺のパソコンでshellcheckのインストールで8時間以上かかった悪夢は解消されるの?
■ このスレッドは過去ログ倉庫に格納されています
5ちゃんねるの広告が気に入らない場合は、こちらをクリックしてください。

ニューススポーツなんでも実況