固定最小長リストを完全かつエレガントな方法で使用するにはどうすればよいですか?


10

私は現在、次のような関数を扱っています。

foo = (\(a:b:c:d:e:f:_) -> foobar a b c d e f) . (++ repeat def)

つまり、リストが与えられた場合、最初の6つの要素を何かに使用し、リストが6要素未満の場合defは、欠落している要素の代わりとして使用します。これは合計ですが、その一部は(のようにmap fromJust . filter isJust)異なるため、私は好きではありません。私はこれを書き直して、偏りを使用する必要がないようにして、これを得ました:

foo [] = foobar def def def def def def
foo [a] = foobar a def def def def def
foo [a,b] = foobar a b def def def def
foo [a,b,c] = foobar a b c def def def
foo [a,b,c,d] = foobar a b c d def def
foo [a,b,c,d,e] = foobar a b c d e def
foo (a:b:c:d:e:f:_) = foobar a b c d e f

技術的には自分のやりたいことをやっていましたが、今ではこれは巨大な混乱です。よりエレガントで繰り返しの少ない方法でこれを行うにはどうすればよいですか?


2
多分、uncons :: Default a => [a] -> (a,[a])デフォルトのを書いてくださいdef。またはデフォルトtakeWithDef。および/またはビューパターン/パターンの同義語。ただし、これには補助ヘルパーコードの記述が必要です。
チー

@chi私はそれで行くだろうと思います。答えてくれれば受け入れます。
ジョセフ・サイブル復職モニカ

2
それだけの価値があるので、の全体論case xs ++ repeat def of a:b:c:d:e:f:_ -> ...は十分にローカルであり、それを使用して、既存の回答で導入されているすべての追加の機構をスキップすることについて二度と考えないでしょう。一般に、私を緊張させるのは、よりグローバルな全体論の引数(たとえば、複数の関数呼び出しにわたって維持される不変式を含む)です。
Daniel Wagner、

takeWithDef正規のリストを返す場合、実際には使用できません。パターンマッチする必要があるためです。-//適切な解決策は、ダニエルが2番目の回答で以下に書いたものです。uncons最初の要素のみを取得するので、それほど役に立ちません。
カイ

回答:


8

安全なパッケージを使用して、たとえば次のように書くことができます。

(!) = atDef def
foo xs = foobar (xs ! 0) (xs ! 1) (xs ! 2) (xs ! 3) (xs ! 4) (xs ! 5)

6

これは少なくとも短いです:

foo (a:b:c:d:e:f:_) = foobar a b c d e f
foo xs = foo (xs ++ repeat def)

パターンが完全であることは簡単にわかりますが、常に終了することを確認するために少し考える必要があります。だからあなたがそれを改善と見なすことができるかどうかはわかりません。

それ以外の場合は少し重いですが、stateモナドでそれを行うことができます。

foo = evalState (foobar <$> pop <*> pop <*> pop <*> pop <*> pop <*> pop)
  where
    pop = do xs <- get
             case xs of [] -> pure def
                        y:ys -> put ys >> pure y

次のような無限ストリームタイプを使用することも想像できます。

data S a = S a (S a)

そして、あなたが構築する可能性があるためfooでアウトrepeat :: a -> S aprepend :: [a] -> S a -> S aおよびtake6 :: S a -> (a,a,a,a,a,a)、そのすべてが合計である可能性があります。おそらく、そのようなタイプが手元にない場合は、おそらく価値がありません。


3
ああ、私はストリームのアイデアがとても好きです。このような中置コンストラクタを使用すると、data S a = a :- S a; infixr 5 :-非常にクリーンに見えます。foo xs = case prepend xs (repeat def) of a:-b:-c:-d:-e:-f:-_ -> foobar a b c d e f
Daniel Wagner、

4

ちょうど楽しみのために(そしてこれはお勧めしません、これはファンシーのためです)、ここに別の方法があります:

import Data.Default

data Cons f a = a :- f a
infixr 5 :-

data Nil a = Nil -- or use Proxy

class TakeDef f where takeDef :: Default a => [a] -> f a
instance TakeDef Nil where takeDef _ = Nil
instance TakeDef f => TakeDef (Cons f) where
    takeDef (x:xs) = x :- takeDef xs
    takeDef xs = def :- takeDef xs

foo xs = case takeDef xs of
    a:-b:-c:-d:-e:-f:-Nil -> foobar a b c d e f

パターンマッチで使用するタイプはtakeDef、見たい要素の数を言うのに自然なタイプレベルを渡すことになります。


1
これは、これまでのところ私の推奨するアプローチです。私はおそらくそれを補完するためにビューパターンを使用するでしょう。(なぜ「非推奨」ですか?短所は何ですか?)
chi

3
型レベルのプログラミングに多額の投資をすると、何がうまくいかなくなるのかを具体的に示しています。1行で即座に理解できるプログラムは、読者がメンタル型推論エンジンに真剣に取り組む必要がある10行に膨らんでいます。
Daniel Wagner、

1
あなたの言ってる事がわかります。私はカウントfoo (takeDef -> a:-b:-c:-d:-e:-f:-Nil) -> foobar a b c d e f1行として。それは再利用のためにいくつかのライブラリにあるべきコードなので、私は残りを数えません。このケースのためだけに書かなければならない場合、あなたが言うようにそれは明らかにやり過ぎです。
chi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.