2009-05-30 15 views
4

機能段落

def get_paraghraps(lines): 
    paragraphs = [] 
    paragraph = [] 
    for line in lines: 
     if line == "": # I know it could also be "if line:" 
      paragraphs.append(paragraph) 
      paragraph = [] 
     else: 
      paragraph.append(line) 
    return paragraphs 

ErlangまたはHaskellでどうやってやりますか?

答えて

4

私は最初のハスケルプログラマーです(そして5年前に学んだ小さなHaskellですが) )(私はわかりやすくするために、種類を追加しました)の周りに渡される:

type Line = String 
type Para = [Line] 

-- Takes a list of lines, and returns a list of paragraphs 
paragraphs :: [Line] -> [Para] 
paragraphs ls = paragraphs2 ls [] 

-- Helper function: takes a list of lines, and the "current paragraph" 
paragraphs2 :: [Line] -> Para -> [Para] 
paragraphs2 [] para = [para] 
paragraphs2 ("":ls) para = para : (paragraphs2 ls []) 
paragraphs2 (l:ls) para = paragraphs2 ls (para++[l]) 

これは動作します:

*Main> paragraphs ["Line 1", "Line 2", "", "Line 3", "Line 4"] 
[["Line 1","Line 2"],["Line 3","Line 4"]] 

だから、ソリューションです。しかし、その後、Haskellの経験は、このようなものを行うためのライブラリ関数は、ほとんど常に存在していることを示唆している:)一つの関連機能をgroupByと呼ばれ、それはほとんど動作します:

paragraphs3 :: [Line] -> [Para] 
paragraphs3 ls = groupBy (\x y -> y /= "") ls 

*Main> paragraphs3 ["Line 1", "Line 2", "", "Line 3", "Line 4"] 
[["Line 1","Line 2"],["","Line 3","Line 4"]] 

おっと。私たちが本当に必要なのは「splitBy」であり、it's not in the librariesが、我々は自分自身を悪いものを除外することができます。

paragraphs4 :: [Line] -> [Para] 
paragraphs4 ls = map (filter (/= "")) (groupBy (\x y -> y /= "") ls) 

か、あなたはクールになりたい場合は、あなたが引数を取り除くと、それを行う得ることができます無意味な方法:

paragraphs5 = map (filter (/= "")) . groupBy (\x y -> y /= "") 

さらに短い方法があると確信しています。:-)

編集ephemient(not . null)(/= "")よりもクリーンであることを指摘しています。だから我々は

paragraphs = map (filter $ not . null) . groupBy (const $ not . null) 

を書くことができます繰り返し(not . null)は、私たちが本当に機能にアウトこの抽象べき強力な指標であり、以下の回答で指摘したように、これは、Data.List.Split moduleが何をするかです。

+0

あなたはそれについての参考文献を持っていますか? Haskell 98のライブラリレポート(http://www.cs.auckland.ac.nz/references/haskell/haskell-library-1.4-html/list.html)*は、これは等価述語ですが、明示的に実装していますgroupBy関数groupByの型シグネチャには "Eq"という制約はなく、任意の(推移的な)述語関数で動作するはずであることを示唆しています...このように使用するのが一般的です:http://www.haskell .org/haskellwiki/List_function_suggestions#Generalize_groupBy_and_friends – ShreevatsaR

+2

私は 'notを使うのが好きです。 '(= =" ")'を使用することになります。これにより、さらにポイントフリーの 'paragraphs = map(filter $ not。null)につながります。 groupBy(const $ not。null) ' – ephemient

3

再帰的に考える。

get_paragraphs []  paras para = paras ++ [para] 
get_paragraphs ("":ls) paras para = get_paragraphs ls (paras ++ [para]) [] 
get_paragraphs (l:ls) paras para = get_paragraphs ls paras (para ++ [l]) 
+1

Oh my!それは私が見る[Paras](http://bulbapedia.bulbagarden.net/wiki/Paras)ですか? –

4

私はまた、ハスケルを学ぼうとしています。この質問の解決策は、Data.Listの機能を使用しています。

paragraphs :: [String] -> [[String]] 
paragraphs [] = [] 
paragraphs lines = p : (paragraphs rest) 
    where (p, rest) = span (/= "") (dropWhile (== "") lines) 

私が使っているものはすでにPreludeから入手できますが、あなたはリンクでその文書を見つけることができます。

考え方は、span (/= "")を使用して最初の段落を見つけることです。段落とそれに続く行が返されます。次に私がrestと呼ぶ小さなリストのリストを再帰します。

最初の段落を分割する前に、dropWhile (== "")を使用して空の行を削除します。これは段落を区切る空の行を食べることが重要です。私の最初の試みは、このでした:

paragraphs :: [String] -> [[String]] 
paragraphs [] = [] 
paragraphs lines = p : (paragraphs $ tail rest) 
    where (p, rest) = span (/= "") lines 

しかし、我々はrest以来の最後の段落は、空の文字列さに達したとき、これは失敗します。

 
*Main> paragraphs ["foo", "bar", "", "hehe", "", "bla", "bla"] 
[["foo","bar"],["hehe"],["bla","bla"]*** Exception: Prelude.tail: empty list 

が空行の削除がこれを解決し、そしてそれはまた、コードの御馳走を作ります段落区切り記号として任意の数の空行がありますが、これはユーザーとして期待しています。

+1

'null'は、空のリストや文字列との等価性をテストするよりも、ほんのわずかしか効率的ではありません。 'span ==" ")' => 'dropWhile null'はどちらも読みやすさを向上させます。 – ephemient

+0

はい、いいえ - 文字列のリストで空の文字列を検索するとき、 ""を検索すると読みやすくなります。少なくとも私のような初心者のために:-) –

3

groupByからData.Listまでは行をグループ化したいと考えています。カスタム関数を使ってどの行が "等しい"かを判断するので、同じ段落の行を "等しくする"ようなものを供給することができます。例えば:inparaのみ隣接する2本のラインを比較することができ、より複雑なロジックがgroupByによって与えられる枠組みに収まらないので

import Data.List(groupBy) 

inpara :: String -> String -> Bool 
inpara _ "" = False 
inpara _ _ = True 

paragraphs :: [String] -> [[String]] 
paragraphs = groupBy inpara 

これは、いくつかの制限を有しています。より基本的な解は、より柔軟性があります。基本的な再帰のいずれかを使用して書くことができる:

paragraphs [] = [] 
paragraphs as = para : paragraphs (dropWhile null reminder) 
    where (para, reminder) = span (not . null) as 
          -- splits list at the first empty line 

spanが供給される関数は偽(最初の空行)になる時点でリストを分割し、dropWhileが供給関数が真(先頭の空行)されている主要要素を削除します。

4

もっとも清潔な解決策は、splitパッケージから適切なものを使用することです。

最初にインストールする必要がありますが、その後はData.List.Split.splitWhen nullというジョブを完全に実行する必要があります。

0

import Data.List.Split (splitOn) 

paragraphs :: String -> [[String]] 
paragraphs s = filter (not . null) $ map words $ splitOn "\n\n" s 

paragraphs "a\nb\n\nc\nd"    == [["a", "b"], ["c", "d"]] 
paragraphs "\n\na\nb\n\n\nc\nd\n\n\n" == [["a", "b"], ["c", "d"]] 
paragraphs "\n\na\nb\n\n \n c\nd\n\n\n" == [["a", "b"], ["c", "d"]]