如何以完整而优雅的方式使用固定最小长度的列表?

Jos*_*ica 10 haskell pattern-matching

我目前正在处理这样的功能:

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

换句话说,给定一个列表,它将使用前六个元素来表示某项内容;如果列表的长度少于六个元素,则将其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
Run Code Online (Sandbox Code Playgroud)

从技术上讲,我做了我想要的,但是现在这是一个巨大的混乱。我该如何以更优雅,更不重复的方式进行操作?

Dan*_*ner 8

使用安全包,您可以编写例如:

(!) = atDef def
foo xs = foobar (xs ! 0) (xs ! 1) (xs ! 2) (xs ! 3) (xs ! 4) (xs ! 5)
Run Code Online (Sandbox Code Playgroud)


Dav*_*her 6

这至少要短一些:

foo (a:b:c:d:e:f:_) = foobar a b c d e f
foo xs = foo (xs ++ repeat def)
Run Code Online (Sandbox Code Playgroud)

您可以轻松地看到这些模式是详尽无遗的,但是现在您必须仔细考虑一下它总是会终止。因此,我不知道您是否可以认为这是一种改进。

否则,我们可以使用state monad来做到这一点,尽管它有点重量级:

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
Run Code Online (Sandbox Code Playgroud)

I could also imagine using an infinite stream type like

data S a = S a (S a)
Run Code Online (Sandbox Code Playgroud)

because then you could construct foo out of repeat :: a -> S a, prepend :: [a] -> S a -> S a, and take6 :: S a -> (a,a,a,a,a,a), all of which could be total. Probably not worth it if you don't already have such a type handy.

  • 哦,我非常喜欢串流创意。使用像`data S a = a:-S a;这样的infix构造函数。infixr 5:-`看起来很干净;`foo xs = a:-b:-c:-d:-e:-f:-_-&gt; foobar abcdef的大小写前缀xs(rep def)。 (3认同)