[表示 : 全て 最新50 1-99 101- 201- 301- 401- 501- 601- 701- 2chのread.cgiへ]
Update time : 04/25 05:23 / Filesize : 232 KB / Number-of Response : 786
[このスレッドの書き込みを削除する]
[+板 最近立ったスレ&熱いスレ一覧 : +板 最近立ったスレ/記者別一覧] [類似スレッド一覧]


↑キャッシュ検索、類似スレ動作を修正しました、ご迷惑をお掛けしました

関数型プログラミング言語Haskell Part17



1 名前:デフォルトの名無しさん mailto:sage [2012/01/02(月) 22:19:28.26 ]
haskell.org
ttp://www.haskell.org/

日本語サイト
ttp://www.sampou.org/cgi-bin/haskell.cgi
ttp://www.shido.info/hs/

過去ログ
関数型プログラミング言語Haskell
Part1 ttp://pc.2ch.net/tech/kako/996/996131288.html
Part2 ttp://pc2.2ch.net/test/read.cgi/tech/1013846140/
Part3 ttp://pc8.2ch.net/test/read.cgi/tech/1076418993/
Part4 ttp://pc8.2ch.net/test/read.cgi/tech/1140717775/
Part5 ttp://pc8.2ch.net/test/read.cgi/tech/1149263630/
Part6 ttp://pc11.2ch.net/test/read.cgi/tech/1162902266/
Part7 ttp://pc11.2ch.net/test/read.cgi/tech/1174211797/
Part8 ttp://pc11.2ch.net/test/read.cgi/tech/1193743693/
Part9 ttp://pc11.2ch.net/test/read.cgi/tech/1211010089/
Part10 ttp://pc12.2ch.net/test/read.cgi/tech/1231861873/
Part11 ttp://pc12.2ch.net/test/read.cgi/tech/1252382593/
Part12 ttp://hibari.2ch.net/test/read.cgi/tech/1272536128/
Part13 ttp://hibari.2ch.net/test/read.cgi/tech/1286706874/
Part14 ttp://hibari.2ch.net/test/read.cgi/tech/1299385928/
Part15 ttp://hibari.2ch.net/test/read.cgi/tech/1310199414/
Part16 ttp://toro.2ch.net/test/read.cgi/tech/1317958045/

303 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 17:14:45.72 ]
そんなDQN本だったっけと思って自分も見返してみた

p.50より引用
> tailコマンドの作りかたにもいろいろありますが、今回は次のような方針で実装しました。
> 1. 行のリストを逆順にする
> 2. 先頭からn行を取る
> 3. 取った部分リストをまた逆順にする

実際のコード、同ページのリスト2.9
> main = do cs <- getContents
> putStr $ lastNLines 10 cs
> lastNLines n cs = unlines $ takeLast n $ lines cs
> takeLast n ss = reverse $ take n $ reverse ss

ここまでに使った関数は putsStr, putStrLn, print, length, take, reverse, lines, unlines
アクションが getContents

入門書の序盤のリスト処理の例題としてはこんなもんじゃないだろうかと思う
ふつける擁護とかしても仕方ないので俺ならこう実装するってのがあれば見てみたい

304 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 17:17:14.40 ]
コードのインデントが崩れたけど見逃して

305 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 17:26:38.48 ]
>>303
そうだよね。

おそらく「ふつうの」のネタ元と思われる「Software Tools」(ソフトウェア作法、
Fortran 版)にも tail を作る演習問題があって、解答は書いてないけどランダム
アクセスの話よりずっと前に出てくるので、頭から読むことが期待されていると思う。




306 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 17:26:50.51 ]
>>290
そりゃ、reverse使ってるからな
Haskellは、アルゴリズムを考えないと本当に遅いし、同じアルゴリズムなら、LLなんて比較にならないくらい早いコードが簡単に書ける
(ただし、ghci/runghcではなく、ghcで実行ファイルになったときに限る)


307 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 17:34:33.10 ]
>>306
じゃあ tail コマンドを実装するにはどういうアルゴリズムが良いんだろ
という話に若干移りかけている

308 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 17:41:39.84 ]
>>307
うーむ・・・
自分もアルゴリズムにそんなに詳しくないんだけど、10行と言う縛り固定だったり、何行表示するかコマンド引数で取れるなら、last関数を複数回使ったほうが速そうではある
(EOF見つけるまで関数適用の必要ないと推測)



309 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 18:18:22.72 ]
こういうのもIterateeがもっと整備されてくれば過去の笑い話になるんだろうな。

310 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 20:17:33.21 ]
必要な行数だけバッファリングしといて、
EOFきたらバッファを全部出力するのが、
メモリ効率的に有利だろう。O(1)で済むので。
getContentsしたらO(n)になってしまう。
常に「指定した行数 >≒ ファイルの行数」ならどっちも同じだが。

311 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 20:35:54.39 ]
必要な行に辿りつくまでの計算量はどこへ行ったんだ



312 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 20:45:16.49 ]
unsafePerformIO使っていいんなら、普通にファイルの後ろから読んでできるな。

使わんかったら無理っぽい。俺には無理。

313 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 20:46:42.49 ]
空間計算量だよ。

seek可能なら後ろから舐めることもできるが、
泥臭いコードになるからチュートリアル向けのコードじゃないな。

314 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 20:57:18.53 ]
>>313
ああ、必要なメモリの量もO記法で表すのか。ごめん

315 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 20:58:16.55 ]
seekが制限されると、他の言語でもパフォーマンス出せないと思うが。



316 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 21:06:37.77 ]
そんなこといちいち書きこむなよ。

317 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 21:09:34.27 ]
>>312
君の考えた方法で、unsafePerformIO はどこに使うの?

318 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 22:32:20.73 ]
unsafePerformIOとかhSeekとか出てるんだから、ちょっと考えれば
わからない?

大したコードじゃないよ。5行くらいで終わる。

319 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 23:01:59.53 ]
>>310

空間計算量なら

takeLast n xs = diff x (drop n xs) where
 diff xs [] = xs
 diff (x:xs) (y:ys) = diff xs ys

みたいなの使えばgetContents使ってもO(1)で済むよ


320 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 23:04:16.00 ]
>>318
unsafePerformIO って要らなくない?

321 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 23:08:59.31 ]
unsafeでなくてはならない意味がわからないんですけど。



322 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 23:18:32.67 ]
>>320
要らない。さっきようやく気づいた。
まだ、Haskellになれてないもので、、、

でも、Haskellっぽく遅延評価で取り出そうと思うと、
unsafe使わざるをえんね。

まぁ、それを意図してるんだろうけど。

323 名前:デフォルトの名無しさん mailto:sage [2012/01/22(日) 23:32:52.07 ]
>>319
モナらないと無理。
getContentsの中身はunsafePerformIOなんで。

324 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 00:13:13.59 ]
unix の tail コマンドなどを Haskell で実装する場合、
もし hSeek 使って逆から読み取ることをするとなると、
読み込むファイルが utf-8 とかだったらどうするの?

今試しに hSeek で戻りながら hGetChar で1文字ずつ取ろうとしたら、
戻るバイト数が分からんかった

325 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 00:14:23.33 ]
あ、すまん
バイナリモードで読み取れば良いのか

326 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 02:32:36.04 ]
>>323

ghc7.0.2で試してみたけど最適化なしでも空間使用量は一定だったよ


327 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 07:08:50.63 ]
>>323
getContentsの中身ってunsafeInterleaveIOじゃないの?

328 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 07:31:08.49 ]
iterateeやcondoit使っときなさいって

329 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 10:13:37.62 ]
tailをHaskellで実装する意義が判らん。
初学者でも出来るような事は、Cみたいな言語で充分じゃないの?

330 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 10:19:33.95 ]
そういう台詞は実際にコード貼ってから言えよ
負け惜しみにしか見えん

331 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 11:02:10.67 ]
意外と人多いのね、ここ。

実際に役に立つプログラムにすることを考えると、tail コマンドって
$ bzcat backup.bz2 | tail -10
みたいにパイプで使うことが多いから、結局頭から読む版も作る必要があるよ。

教科書でやるなら、素直に頭から読む版を紹介して、ランダムアクセス版は
練習問題にするのがいいだろうね。



332 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 11:21:41.80 ]
>>329
HaskellでできることをわざわざCでやる必要がないだろ

333 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 11:52:08.14 ]
わざわざ泥臭いことをやってはいけないという空気に従うのが逆に面倒だな

334 名前:デフォルトの名無しさん [2012/01/23(月) 14:22:10.66 ]
具体化できない奴がやたら抽象化にこだわるみたいな

335 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 19:22:03.97 ]
>>334
そのできない具体化とやたら拘る抽象化の例を挙げてみて

もしかして具体例もなく、なんとなく抽象的にレスしてるだけとか?

336 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 20:32:55.92 ]
盛り上がってきたね。

別にプログラムなんて好きなんで作ればいいんじゃない?


337 名前:デフォルトの名無しさん [2012/01/23(月) 21:10:58.12 ]
>>335
読解力が爆発してますね
>>333へのレスです

338 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 21:30:27.63 ]
>>309
イテレーティ解説サイト教えてください!(日本語の)

339 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 21:30:48.60 ]
>>337
だからさ、お前が「〜みたいな」って自分で言ってるじゃん
その抽象的な「みたいな」をもっと具体的に説明してくれよ

じゃないと、それが >>333 の喩えにちゃんとなっているのか、
あるいは >>334 が勝手に喩えていると勘違いしてるだけで、
何も喩えられていないのか、判断に困るだろ

340 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 21:35:22.98 ]
>>338
「iteratee haskell」で日本語サイトをググってみたか?

たとえば d.hatena.ne.jp/mkotha/20111106/1320584724
こんなページがヒットしたんだが



>>337
「具体化できない奴がやたら抽象化にこだわる」事がどういう事か、
自分でもあまりよく分かって(頭の中で整理できて)ないんだろ?
だから、例えばこういう奴とか言って具体例が出てこないんだろ?

341 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 21:48:59.93 ]
抽象化にこだわってるかどうかは知らんが、
とにかく具体的なコード書く能力が無いのは事実だな
「シーク禁止じゃなければ速いtail書けるんだけど、でも泥臭くなるから……(ぶつぶつ」
とか言いながら実際にはシーク使ったコードは書けない、みたいな



342 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 21:55:46.74 ]
case of で、あるマッチングと別のマッチングで全く同じ式を計算させたい場合、
どう式を書くのがより合理的なのでしょうか

例えば次のような感じです

data T = A Char | B Bool | C Bool | D Int | E String

f :: T -> Int
f x = case x of
 A d -> g1 d
 B d -> g2 d
 C d -> g2 d
 otherwise -> 0

パターン B b とパターン C d の場合が同じ式になっています

この例の場合、レスの文字数などの関係で g2 d というように関数を適用してますが、
実際は別の関数にするほどでもなく、それでいて短くもないような、
例えば let が2〜3行に in が1行程度の式だったりします

2つのパターンの場合に同じ式を計算する事を分かりやすく示したいのですが、
C言語の switch case のフォールスルーみたいには書けないですよね

コピペのミスなどを防ぐには、この例のように関数にまとめて、
それを呼ぶだけの式にするくらいしか手はないでしょうか

343 名前:デフォルトの名無しさん [2012/01/23(月) 22:00:11.07 ]
>>339
具体化=泥臭い=hSeekでシコシコ読み込む方法と、すでに出ていることから察するに、
恐らく>>333へ文句言ってると受け取って、こっちの文意が伝わってないのかなと思った
でも>>339では喩えになっているかって言ってるもんな
何かトンデモないこと言ってしまったんだろうか
たしかに>>334は毒づきすぎたから、そこはごめんなさい
議論の邪魔して失礼しました

344 名前:デフォルトの名無しさん mailto:sage [2012/01/23(月) 22:13:40.06 ]
誰かhSeek でスマートに実装したtailください

345 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 07:56:12.73 ]
>>333
言いたいことは分かるが、ある程度は仕方がない

数回経験すれば自然と分かることだが、
Haskellで泥臭くやると、C言語などで泥臭くやる場合以上に
面倒で、スパゲッティで分かりにくくなり、かつメンテが難しくなる

Haskellで泥臭くやるということは、手続き的な、
つまり計算「順序」に過剰に縛られたプログラムを書くということ
順序に縛られているから計算が一本の長い紐になり、
それがソース上の関数群を複雑に縫い止める

C言語などなら、そこから少しずつリファクタリングし、処理単位の小さくし、
縫い合わされた巨大な処理を細かくばらすことは良くある

でもHaskellでは一度複雑に縫い止めた関数群を少しずつばらすのは容易ではない
計算順序を意識しすぎた為にそうなったのであり、その意識を変えなければ無理
考え方そのものを変えた場合、少しずつ修正するよりは全てやり直した方が早い

そうすると、後で全てやり直すくらいなら、初めから意識を変えて、
計算順序に過剰に縛られないように作ることに意識が向くようになる

初心者のうちは「意識を変えなければならない」という事が面倒でたまらないが、
そのうち「わざわざ」泥臭いことをやってはいけないという意識は消えていく
自然と泥臭いことをしなくなる

346 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 11:09:18.39 ]
>>345
333じゃないけど、それは自分の実感と全然違うな
Haskellで手続き的なコードを書くのは良くやるけど、
Cでやるのに比べて面倒だと思うことはほとんどない
多次元のmutable配列とかがHaskellだと構文的にごちゃごちゃするくらい

リファクタリングについては、なんでそんな結論に至ったのか理解に苦しむ
局所関数定義と高階関数を自在に使える時点でCとは比較にならない

そもそも「計算順序を意識」するのを止めるだけで関数的に綺麗なアルゴリズムが出てくる訳でもない
時間O(1)のtailを実装するには結局seekするしかないんだよ

347 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 12:11:52.41 ]
だれかHaskellのスパゲティ屋さんのプログラム見せて

348 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 12:33:17.39 ]
>>347
>>46

349 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 12:34:10.82 ]
>>346
「計算順序を意識」とは言っていない

「計算順序を意識しすぎる」や「計算順序に過剰に縛られた」と言っている

意識しなければ正しい計算ができない事は承知している

350 名前:349 mailto:sage [2012/01/24(火) 12:49:06.27 ]
それと

> 時間O(1)のtailを実装するには結局seekするしかないんだよ

分かっている
だから私はtailの時間O(1)の実装には一言も触れていない

>>345 では、わざわざ泥臭いことをやってはいけないという空気になりがちな
私なりの理由を述べた(tailの実装といった具体的な対象ではなく一般論)

やってはいけないとは言いすぎだと思うが、アプリの設計開始時から、
または Haskell によるプログラム開始時から
泥臭いことをやってはいけないというくらいの考えでプログラムした方がいいと思う
少なくとも Haskell では

泥臭い事を考えるのは、そうしなければ目標を達成する術が残っていない場合だけ
本当に最後の最後に考える最終手段くらいのもの

本来はもっと抽象度を高める方向に意識を向けた方が良いと思う
例えば seek で1バイトずつ取得したものを文字と解釈して計算するよりも、
getContentsに任せて「ファイルの内容を計算対象にする」方が抽象度は高い

蛇足かも知れんが、ついでに言えば入門書も抽象度を高める考え方や方法を伝授すべきだと思う

351 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 14:10:06.15 ]
>>350
お前がそういう意見なのは全然構わん
>>345が「Haskellは泥臭いことをやるのには向かない」と
言っているように読めたので反論しただけ
個人的には泥臭いのを嫌がっていては実用コードは書けないと思っているから



352 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 15:21:05.01 ]
>>348
TX
クラクラ来た。

353 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 15:36:49.63 ]
>>350
>本当に最後の最後に考える最終手段くらいのもの

最初でも最後でもどっちでもいいんじゃないの
なぜ順序を決めたがるのか

354 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 16:01:49.74 ]
>>340
あざっす^^

355 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 19:15:01.19 ]
>>353
俺は >>350 じゃないので横レスになるが、決める決めないじゃなくて、言語の文化としてより自然なやり方はあると思う。
例えて言うなら、日本語は格助詞があるから語順はある程度自由なはずだが、やっぱり自然な語順はあるだろ。
そんな感じ。

やりたい奴はもちろん最初っから泥臭く書いたってそりゃかまわないが、
たいした理由もないのにあえて泥臭く書くのでは Haskell を使う甲斐が無い。

356 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 20:34:12.21 ]
つまりムダなIO発生させていい気になってるってことねw
トイならそれでもいいけど実用でそれやったらアホだぞ
どれだけキャッシュの無駄遣いすると思ってんだか

357 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 20:55:06.07 ]
別に抽象度高めつつ、無駄なキャッシュも減らすようにできると思うんだが、
どっちもどっちだな〜。

どっちもあほ。

358 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 21:08:23.56 ]
>>357
(ふりだしにもどる)
数GBあるログをtailするコードをHaskellで書いてみてくれ

359 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 21:09:07.58 ]
とりあえず叩き台としてコード書いてみたよ。

tailFile n h = do
 seekable <- hIsSeekable h
 if seekable
  then do
   sz <- hFileSize h
   hSeek h AbsoluteSeek $ max 0 (sz - n)
   hGetContents h
  else do
   takeLast n <$> hGetContents h

takeLast n xs = diff xs (genericDrop n xs) where
 diff xs [] = xs
 diff (_:xs) (_:ys) = diff xs ys


360 名前: [―{}@{}@{}-] デフォルトの名無しさん mailto:sage [2012/01/24(火) 22:17:01.53 ]
d.hatena.ne.jp/slayer845/20120123/1327328227
性能は出ているらしい
conduit読めないから俺にはいまいち何やってるか分からんが

361 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 23:33:17.89 ]
批判組早く



362 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 23:42:03.26 ]
手続き型の言語による実装と抽象度では大差ないか劣るのではないか

363 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 23:43:15.57 ]
>>359
行数指定できないから0点

364 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 23:55:11.19 ]
晒した勇気に100点をあげたい。

365 名前:デフォルトの名無しさん mailto:sage [2012/01/24(火) 23:58:01.89 ]
間とって50点

366 名前:デフォルトの名無しさん mailto:sage [2012/01/25(水) 00:21:24.87 ]
相乗平均なら0

367 名前:デフォルトの名無しさん mailto:sage [2012/01/25(水) 01:31:32.52 ]
行数指定時用のコードを書いみたよ。

なんとなく遅延IO使ってみたよ。Iteratee使えるならそっちの方がいいんじゃないかな。

hGetLinesReversed :: Handle -> IO [String]
hGetLinesReversed h = hFileSize h >>= lazyLoop h [] where
 lazyLoop h rest pos = unsafeInterleaveIO $ loop h rest pos
 loop h rest pos
  | pos < 0 = return [rest]
  | otherwise = do
   hSeek h AbsoluteSeek (max (pos-blockSize) 0)
   xs <- replicateM (fromInteger (min blockSize pos)) (hGetChar h)
   let (hd:tl) = lines (xs++rest)
   (reverse tl ++) <$> lazyLoop h hd (pos - blockSize)
 blockSize = 128

368 名前:デフォルトの名無しさん [2012/01/25(水) 02:10:21.02 ]
遅延IOとな
結局unsafe使う以外に方法って見つかってないんだよね?

369 名前:デフォルトの名無しさん mailto:sage [2012/01/25(水) 04:13:13.51 ]
普通の getContents とかも遅延IOだけど……


370 名前:デフォルトの名無しさん mailto:sage [2012/01/25(水) 06:30:19.08 ]
>367
これを使ってO(1)のtailを作れたら神だな。


371 名前:デフォルトの名無しさん mailto:sage [2012/01/25(水) 07:24:35.01 ]
>>368>>367 が unsafe 系を使ってる理由を
「処理速度を出す為」だと思ってるんじゃないだろうか



372 名前:デフォルトの名無しさん mailto:sage [2012/01/25(水) 07:57:17.27 ]
>>370
普通に書けばファイルサイズに対してO(1)時間になるんじゃね?

373 名前:デフォルトの名無しさん mailto:sage [2012/01/25(水) 21:53:21.47 ]
unsafe使わずに書いてみた。
ファイルサイズチェックしてないので、10行無かったり、小さすぎるのは駄目だけどね。

main = do
h <- openFile "test.log" ReadMode
hGetLinesTail h 10 0 >>= putStrLn
hClose h

blockSize = 128
hGetLinesTail h l pos = do
xs <- hGetContentsSeekFromEnd h pos
if l > (length$lines xs) then hGetLinesTail h l (pos + blockSize)
else return xs

hGetContentsSeekFromEnd h pos = do
hSeek h SeekFromEnd (-pos)
replicateM (fromInteger pos) (hGetChar h)


374 名前:デフォルトの名無しさん mailto:sage [2012/01/25(水) 21:58:39.36 ]
すまん。インデントがずれてしまった、、、


main = do
  h <- openFile "test.log" ReadMode
  hGetLinesTail h 10 0 >>= putStrLn
  hClose h

blockSize = 128
hGetLinesTail h l pos = do
  xs <- hGetContentsSeekFromEnd h pos
  if l > (length$lines xs) then hGetLinesTail h l (pos + blockSize)
  else return xs

hGetContentsSeekFromEnd h pos = do
  hSeek h SeekFromEnd (-pos)
  replicateM (fromInteger pos) (hGetChar h)

375 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 15:33:16.32 ]
その言語の背景にあるパラダイムを上手に活用できればスマートな
プログラムになるだろうけど、
強引に別のパラダイムのものに合わせようとすると悲惨になるのは当然じゃろ?
そのパラダイムに応じた方法を模索していくしかないような。
Haskellって他よりパラダイムが明確なだけに得手不得手があるのは
仕方がなさそうよね。

376 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 16:55:41.77 ]
Unixコマンドは標準入出力をストリームとして扱う点で
関数型言語のパラダイムと親和性があると思うけどな
Broken Pipe

377 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 17:41:23.30 ]
>>375
結局、メタパラダイムとかいうのは的外れってことだね

378 名前: [―{}@{}@{}-] デフォルトの名無しさん mailto:sage [2012/01/26(木) 19:26:22.04 ]
Haskellは関数型+手続き型なのでそんなに極端な得手不得手はない

379 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 20:04:34.51 ]
そんなに極端な得手不得手はないが、悲惨なことになるのか...

380 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 20:23:58.12 ]
>>376
標準入出力をストリームとして扱うのはインターフェースの部分
内部では tail が代表するように単純にストリームでは扱っていない

関数型は無限リストや継続などでストリームを上手く表現できるが、
今回話題になっている話はインターフェースじゃなくて内部処理の方

正方向で流れてくる情報を逆方向からたどるというのは、
ある意味ストリームとは対極にある処理だと思う

内部処理もストリームにする事に拘るなら、
逆方向にする時にバッファにため込む必要がある
(たまたま Haskell では遅延評価のおかげでそのバッファ処理を自動化できるが)

381 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 20:33:13.40 ]
正格評価でもバッファぐらい普通にできるが?



382 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 20:43:46.18 ]
>>381
正格評価ではバッファを普通にはできない、
と言っているように感じたのか

それなら申し訳ない

遅延評価という仕組みを実現するためのGHCの実装のおかげで、
バッファを作らなきゃいけないことを意識する必要がない
という意味で言ったのだ

念のために言い添えておくと、処理速度などの話とは全く別のことだ

383 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 21:37:21.78 ]
遅延評価とバッファの関係がよくわからないな。
少なくとも>374のコードは再帰するたびに毎回ファイル末尾から読み直してるようにみえる。
行のサイズ = blockSizeのファイルに対して、
tail nを計算するのにO(n^2)の処理を行っているのでは?

384 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 22:05:50.63 ]
>>383
> 遅延評価とバッファの関係がよくわからないな。

>>374 のコードは今回の私の話とは全く関係ない
>>376 のストリームの話だ

逆順に指定行数を出力するという処理を「ストリームとして処理」するなら、
順に流入してきたデータを全て一時的にどこかに溜めて(バッファ)、
溜め終わってから、そこのデータの末尾から順に流出させる必要がある

そのバッファをどうやって作るか、どうやってデータの出し入れをするか、
ということが問題になるが、遅延評価を実現する仕組みのおかげで、
Haskell(の大抵の実装)ではそれを意識しなくてもできてしまう
上の方のレスで問題視された「ふつける」に載ってる方法だ

getContents 関数から流出した(リストで表現された)ストリームが
reverse 関数に流入してくる

遅延評価の仕組みにより、reverse 関数はリストの内容は評価しない
後で評価する可能性がある未評価のデータはメモリ内に残り続ける
この部分がストリームを逆順にたどるための「バッファ」の役割を果たしている

385 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 22:51:34.19 ]
>>383
おっしゃるとおり。
とりあえず、unsafe使わずに書けるかやってみただけで効率は良くない。
ついでに、ちょっと間違ってて10行以上取ってきます。
遅延IO使えばもっとシンプルに効率よく取ってこれるよ。

ちなみに、遅延評価とバッファは全く関係ない。
ファイルを逆から読むのにバッファは必要ない。

386 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 23:11:31.79 ]
>>385
> ちなみに、遅延評価とバッファは全く関係ない。
> ファイルを逆から読むのにバッファは必要ない。

ファイルを逆から読むのにバッファを必要としない方法もあることは分かっている

>>384 で私が言ったのは、「ストリームとして扱う場合はバッファを必要とする」
ということだ

ストリームというのは、パケット通信などのように、
ある塊のデータ群が一列に順に一つずつ流れる、あるいは流すこと

順に流入してきたものを逆順に流出させるには、バッファが絶対に必要だ
そのバッファの仕組みをプログラマが意識して自分で作る必要があるかどうかは別だが

確かに遅延評価とバッファに直接の関係はない
だが、上記の「ストリームの逆順流出」において
遅延評価を実現させる為の内部の仕組みがたまたまバッファの役割を果たす

もう一度言うが、あくまで >>376 のレスを受けて、
「内部処理もストリームとして扱う場合」の話をしている
(それが非効率的な事も分かっている)

387 名前:デフォルトの名無しさん mailto:sage [2012/01/26(木) 23:42:32.23 ]
よくわからん。
もしhGetContentsが遅延評価でなく先行評価だったとしたら、
その「ストリームの逆順流出」プログラムはどう変わるんだ?
どっちにしてもプログラムはhead n $ reverse $ ...
なんじゃないのか。


388 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 00:05:03.45 ]
>>387
> もしhGetContentsが遅延評価でなく先行評価だったとしたら、
> その「ストリームの逆順流出」プログラムはどう変わるんだ?

そんなことは知らん
先行評価だった場合にGHCが内部でどのような実装をしてくるのか俺には分からない

初めの >>380 では端折ったが、>>382 からはずっと、
遅延評価を実現する為に使われている内部の仕組みが、
ストリームとして流れてくるデータ順の逆転という処理において
バッファとして機能することだけを話しているんだが

ちなみに、この目的で内部の仕組みがバッファとして機能するのは、
hGetContents の処理ではなく reverse の処理をGHCが実行ファイルに埋め込む部分だ


389 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 00:07:50.19 ]
おれは言わんとしてることがわかったけど、
もう少しわかりやすく説明したほうがいいと思うよ。

390 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 00:20:31.26 ]
>>389
了解した、努力する

391 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 00:21:30.05 ]
説明ぷりーず。
一応、WHNF簡約やGHCのSTGマシンあたりの実装は理解してるつもり。



392 名前:デフォルトの名無しさん [2012/01/27(金) 00:43:19.91 ]
チュートリアルをやってるのですが、最初で詰まってます。
以下のboomBangsという関数定義でチュートリアルのままなのですが、自分の環境だとエラーになります。
どこが間違ってるのでしょうか。ghci 6.12.1です。
Prelude> boomBangs xs = [if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]

<interactive>:1:13: parse error on input `='
Prelude>

どうかよろしくお願いします。


393 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 00:55:24.00 ]
Prelude> let boomBangs xs = [if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]

394 名前:デフォルトの名無しさん [2012/01/27(金) 01:05:23.28 ]
動きました。ありがとうございました。
Prelude> let boomBangs xs = [if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
*** Parser:
*** Desugar:
*** Simplify:
*** CorePrep:
*** ByteCodeGen:
Prelude> boomBangs [1,2,3,4,5]
*** Parser:
*** Desugar:
*** Simplify:
*** CorePrep:
*** ByteCodeGen:
["BOOM!","BOOM!","BOOM!"]


395 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 01:13:49.09 ]
>>391
あんまり難しい話じゃなくて、C言語とかでも、Seekなしで、
ファイルの先頭からシーケンシャルアクセスしかできないとしたら、
最後10行のデータ取ろうと思うと、どうしても直近10行はバッファ
しとかないといけないでしょ?

Haskellは、このバッファを意識しないでも、自動でやってくれる
って言ってるんだと思うよ。

遅延評価がバッファしてるわけじゃないけど、バッファしてるように
見えなくもない。

396 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 02:26:27.98 ]
Haskellも結局クリーンで美しいカルト言語だったんだお
設計外のことを無理にやらせれて破綻するんだお

397 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 02:49:49.78 ]
ということにしたいのれすね?

398 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 03:01:28.89 ]
実際にやってみたけど、「ふつうの」に書いてある元々の「効率の悪い」
tail に 10 GB ていどのファイルを食わせても、5 秒もかからないんだよね。
これが分単位になるのなら考えてもいいけど、そうでなければ簡潔さの
ほうが大事だと思う。

“「効率」についてうんぬんする人々は、たいていの場合主として本番計算に
計算機資源がどのくらい使われるか、という点にばかり着目していて、コンパ
イルしたり、虫取りしたり、その他さまざまなやりそこないをしたりしながら
本番計算にそなえるために消費される人間の時間と計算機の時間を考えに入れ
ていないのである。” (ソフトウェア作法 p.132)





399 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 03:03:28.54 ]
ごめん、10GB は嘘だわ。10MB ね。


400 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 03:13:57.42 ]
ちなみに自分は後ろから読む tail コマンドを昔 C (Pascal だったかも)で書
いてみたことがあるけど、
* ファイルが改行で終わる場合と終わっていない場合を分ける
* バッファの先頭から行がはじまっているか、その前からつながっているかを分ける
とか、細かい場合分けがあって、予想したより面倒だった。



401 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 05:53:27.42 ]
>>395
正格評価でも、明示的にバッファ取らなくても、
パラメータ渡しをバッファ代わりにできますし…




402 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 05:56:04.14 ]
>>398
> これが分単位になるのなら考えてもいいけど、そうでなければ簡潔さの
> ほうが大事だと思う。

んなこたーない。

> “「効率」についてうんぬんする人々は、たいていの場合主として本番計算に
> 計算機資源がどのくらい使われるか、という点にばかり着目していて、コンパ
> イルしたり、虫取りしたり、その他さまざまなやりそこないをしたりしながら
> 本番計算にそなえるために消費される人間の時間と計算機の時間を考えに入れ
> ていないのである。” (ソフトウェア作法 p.132)

これは数回実行するだけの研究用プログラムについては正しいが、
UNIXのtailコマンドのように1度書かれたソースを莫大な回数実行する場合には
詰められる無駄は詰めたほうがいい。

403 名前:デフォルトの名無しさん mailto:sage [2012/01/27(金) 06:59:03.58 ]
正格な評価でも10行分のキューにストリームから一行読んでは突っ込んでいくだけでしょ
別にC言語でも数分で書けるから特にHaskellを使う利点はないよね






[ 続きを読む ] / [ 携帯版 ]

前100 次100 最新50 [ このスレをブックマーク! 携帯に送る ] 2chのread.cgiへ
[+板 最近立ったスレ&熱いスレ一覧 : +板 最近立ったスレ/記者別一覧]( ´∀`)<232KB

read.cgi ver5.27 [feat.BBS2 +1.6] / e.0.2 (02/09/03) / eucaly.net products.
担当:undef