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


10

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

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

换句话说,给定一个列表,它将使用前六个元素来表示某项内容;如果列表的长度少于六个元素,则将其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
对于它的价值,我认为for的总体论点case xs ++ repeat def of a:b:c:d:e:f:_ -> ...足够本地化,以至于我不会再三考虑仅使用它并跳过现有答案引入的所有额外机制。通常,更全局的总体参数(例如,涉及多个函数调用之间保持不变的变量)使我感到紧张。
Daniel Wagner

takeWithDef如果它返回常规列表,则实际上是不可用的,因为我们需要对以下内容进行模式匹配:-//正确的解决方案是丹尼尔在第二个回答中写的内容。uncons仅获取第一个元素,因此它没有太大用处。

Answers:



6

这至少要短一些:

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

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

否则,我们可以使用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

我也可以想象使用像

data S a = S a (S a)

因为这样你可以构造foorepeat :: a -> S aprepend :: [a] -> S a -> S atake6 :: S a -> (a,a,a,a,a,a),所有这些都可能是全部。如果您还没有方便的类型,可能不值得。


3
哦,我非常喜欢串流创意。像data S a = a :- S a; infixr 5 :-这样的infix构造函数看起来很干净;foo xs = case prepend xs (repeat def) of a:-b:-c:-d:-e:-f:-_ -> foobar a b c d e f
丹尼尔·瓦格纳

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
到目前为止,这是我的首选方法。我可能会使用视图模式来补充它。(?为什么“不推荐”什么利弊?)

3
它确切地体现了您在类型级别的编程上投入大量资金时开始出现的问题:什么是单行的,即时可理解的程序迅速扩展为十行,需要读者认真使用其思维型推理引擎。
丹尼尔·瓦格纳

1
我明白你的意思了。我算是foo (takeDef -> a:-b:-c:-d:-e:-f:-Nil) -> foobar a b c d e f一行。我不计算其余部分,因为它应该在某些库中以供重用。如果只为这种情况而写,那显然就太过分了。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.