2012-03-24 24 views
4

Haskellに次のコードを書くためのより良い、より簡潔な方法がありますか?私はif..elseを使用しようとしましたが、それは以下より読みにくくなってきています。私はxsのリスト(これは巨大です!)を8回通過することを避けたいだけです。要素を8つのグループに分けるだけです。 groupByからData.Listまでは、1つのテスト条件関数、(a -> a -> Bool) -> [a] -> [[a]]しか使用しません。複数のテスト関数を持つgroupBy

x1 = filter (check condition1) xs 
x2 = filter (check condition2) xs 
x3 = filter (check condition3) xs 
x4 = filter (check condition4) xs 
x5 = filter (check condition5) xs 
x6 = filter (check condition6) xs 
x7 = filter (check condition7) xs 
x8 = filter (check condition8) xs 
results = [x1,x2,x3,x4,x5,x6,x7,x8] 

答えて

5

これは一度だけリストを横断書き込むことができます。

import Data.Functor 
import Control.Monad 

filterN :: [a -> Bool] -> [a] -> [[a]] 
filterN ps = 
    map catMaybes . transpose . 
    map (\x -> map (\p -> x <$ guard (p x)) ps) 

リストの各要素について、mapMaybeのリストを生成し、それぞれつの述部に対応する。要素が述語を満たさない場合はNothing、述語を満たす場合はJust xです。次に、transposeは、リストが要素ではなく述語によって編成され、map catMaybesが述語を満たさない要素の項目を破棄するように、これらのリストをすべてシャッフルします。

いくつかの説明:x <$ mfmap (const x) mあり、そしてMaybeため、guard bif b then Just() else Nothingので、x <$ guard bif b then Just x else Nothingです。

mapmap (\x -> [x <$ guard (p x) | p <- ps])と書くこともできます。

+0

非常にエレガントですが、私はLandeiの答えがより効率的だと思います。 – is7s

+0

私はそれほど確信していません。まず、Landei'sは何かをする前にリスト全体を逆転させます。 – ehird

+0

他の議論の後で、私はより良い実装を思いついた。私は私の答えを更新しました。 – ehird

2

私はあなたがGHC.ExtsからgroupWithを使用することができると思います。

あなたはxsその「クラスの中のすべての要素を割り当てることがa -> b関数を記述した場合、私はgroupWithは一度だけリストを横断する、あなたはそれがしたいxsだけの方法を分割するでしょう信じて。

3
map (\ cond -> filter (check cond) xs) [condition1, condition2, ..., condition8] 
+0

これで呼び出されるべきリストuppdate各値のために行くだろう各条件ごとにリストを再度トラバースしています。 – vis

+2

しかし、単純な関数を持つ複数のトラバーサルは、複雑な関数を持つ1つのトラバーサルよりも遅いのですか? – augustss

+0

ああ、絶対に。それは機能する単なる最も簡単なものです。 –

1

groupByあなたが望むものは実際にはありません。たとえそれが複数の述語関数を受け入れたとしても、リスト上では何もフィルタリングを行いません。何らかの条件を満たすリスト要素を連続してグループ化するだけです。フィルタ条件が組み合わされても、指定されたリストのすべての要素をカバーしても、これはまだ異なる動作です。たとえば、groupByは、リスト要素の順序を変更したり、結果に複数の要素を含めることはできませんが、操作で両方の要素を実行できます。例として

import Control.Applicative 

filterMulti :: [a -> Bool] -> [a] -> [[a]] 
filterMulti ps as = filter <$> ps <*> pure as 

この機能は、あなたが探しているものでしょうnietakiの答えに補遺として

> filterMulti [(<2), (>=5)] [2, 5, 1, -2, 5, 1, 7, 3, -20, 76, 8] 
[[1, -2, 1, -20], [5, 5, 7, 76, 8]] 
+0

これは洗練されたソリューションのように見えますが、各条件ごとに毎回そのリストが横断されないことを確認できます。 – vis

+0

これは毎回リストをたどりますが、あなたが質問で与えた例もそうです。同じリストを複数回トラバースせずに複数回フィルタリングすることはできません。 – bitbucket

+0

私はそれを "8つのグループに分けて"から取り出しました。リストをフィルタリングする必要はありません。要素を省略せずに別のグループに分割してください。 – nietaki

1

を(これはコメントする必要がありますが、それはあまりにも長いです彼の答えが正しければ、彼を受け入れてください!)、関数a -> bは、一連の入れ子になったif ... then .. elseとして書くことができますが、それはあまり慣れないハスケルではなく、あまり拡張できません。これは少し良いかもしれません:

import Data.List (elemIndex) 
import GHC.Exts (groupWith) 

f xs = groupWith test xs 
    where test x = elemIndex . map ($ x) $ [condition1, ..., condition8] 

それは最初condition_それを満たすことにより、各要素を分類し(そして自分自身のカテゴリにいずれかを満たしていないものを置きます)。

(。elemIndexのドキュメントがhereある)

4

あなたは一度だけリストを横断1を主張する場合は、

filterMulti :: [a -> Bool] -> [a] -> [[a]] 
filterMulti fs xs = go (reverse xs) (repeat []) where 
    go [] acc = acc 
    go (y:ys) acc = go ys $ zipWith (\f a -> if f y then y:a else a) fs acc 
1

最初の関数は、「uppdated」リストのリストを返し、第二の機能は、リスト全体と

myfilter :: a -> [a -> Bool] -> [[a]] -> [[a]] 
myfilter _ [] [] = [] 
myfilter x f:fs l:ls | f x  = (x:l): Myfilter x fs ls 
        | otherwise = l:Myfilter x fs ls 


filterall :: [a] -> [a -> Bool] -> [[a]] -> [[a]] 
filterall [] _ l = l 
filterall x:xs fl l:ls = filterall xs fl (myfilter x fl l) 

これはfilterall xs [condition1,condition2...] [[],[]...]

+0

Haskellの関数名は大文字で始めることができないことを忘れないでください。 – Vitus

関連する問題