什么是更好的方法来写[(-1,-1),( - 1,0),( - 1,1),(0,-1),(0,1),(1,-1), Haskell中的(1,0),(1,1)]?

时间:2014-01-23 23:19:23

标签: haskell list-comprehension applicative

我遇到了一些需要列表的情况:

[(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)] -- no (0,0)

请注意列表中没有(0,0)。我使用(dx,dy)元组从坐标向上,向下,向左,向右和对角搜索。

每次我写它时,我觉得应该有一个更简洁,和/或更容易阅读的方式来定义它。我对Haskell比较陌生,而且我在Applicative / Functor / Monad技巧的某个地方找到了应该有一个巧妙的方法来做到这一点。

我试过了:

[(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]

有时最好把它写出来,但我认为这不是其中之一。一目了然(0,0)不包括在内并且你必须阅读它以注意模式。

map (\[a,b] -> (a,b)) $ delete [0,0] $ sequence $ replicate 2 [-1,0,1]

我喜欢上面那个,因为我把“2”放在那里,这是一个很好的明确方式,说“我们两次做同样的事情”,但我不能接受前面的地图有大不安全的lambda和2个名字。

[(dx,dy) | let a = [-1,0,1], dx <- a, dy <- a, (dx,dy) /= (0, 0)]

这个名称中包含太多名称,但完全按照设计使用列表推导。对于那些真正喜欢列表推导的人来说,阅读起来可能更容易,但我不喜欢所有的名字。

let x = [-1,0,1] in delete (0,0) $ (,) <$> x <*> x

那个看起来更漂亮的imo,但我没有那个“2”,我有一个名字。到目前为止,这是我的最爱,但感觉并不完美。

我想如果我理解如何更好地写这个,我可能会更深入地了解Functors / Monads等。我已经读了很多关于它们的内容,我听过很多像fmap / mconcat / etc这样的词,但我不知道在这种情况下要抓哪一个。

7 个答案:

答案 0 :(得分:25)

实际上,我认为在这种情况下明确写出来确实是最好的。只要合理地对齐它,任何问题都无法保持开放:

neighbours = [ (-1,-1), (-1,0), (-1,1)
             , ( 0,-1),         ( 0,1)
             , ( 1,-1), ( 1,0), ( 1,1) ]

除此之外别无选择。

当然,还有更多简洁的替代品。作为一个物理学家,我倾向于

   [ (round $ - sin φ, round $ - cos φ) | φ <- [pi/4, pi/2 .. 2*pi] ]

这当然是更昂贵的计算,但如果您只在一个地方定义此列表并从您的整个程序重新使用它,则无关紧要。这里的订单不同,不确定是否重要。

答案 1 :(得分:24)

为什么不使用列表理解?它们中可以有布尔警戒,因此排除(0,0)非常简单:

[(i,j) | i <- [-1..1], j <- [-1..1], (i,j) /= (0,0)]

请注意,作为一个Haskell newb,我可能会有一种更紧凑/更有效的方式来编写该保护表达式。尽管如此,这还是完成了工作。

答案 2 :(得分:15)

Prelude Data.Ix Data.List> delete (0,0) (range ((-1,-1),(1,1)))
[(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]

答案 3 :(得分:6)

以下是使用Control.Applicative中的内容的简明版本:

delete (0, 0) $ (,) <$> [-1..1] <*> [-1..1]

就我个人而言,我认为使用&看起来要好得多,$只是(,) <$> [-1..1] <*> [-1..1] & delete (0, 0) 翻转。

liftA2

您也可以使用<$>代替<*>liftA2 (,) [-1..1] [-1..1] & delete (0, 0)

infixl 1 &
x & f = f x

它在镜头库中定义,但您可以自己定义:

{{1}}

所有这一切,我仍然更喜欢具有列表理解的版本,甚至只是具有良好缩进的文字列表。

答案 4 :(得分:2)

liftA2 zip (!!0) (!!1) . transpose . tail . replicateM 2 $ [0,1,-1]

(不是我推荐的。)

答案 5 :(得分:2)

Prelude> let sqrt' n | n == 0 = [0] | otherwise = [sqrt n, negate (sqrt n)]

Prelude> [(x,y) | x <- [-1..1]
                , y <- sqrt' (1 - x^2) ++ (if x==0 then [] else sqrt' (2 - x^2))]

答案 6 :(得分:1)

更易于阅读您可以尝试:

left = (-1,0)
right = (1,0)
up  = (0,1)
down = (0,-1)
--etc
directions = [left,right,up,down]

这些是真正的向量,因此如果在您的应用中有意义,或者创建自己的自定义向量操作,您可能需要考虑使用矢量库:

import Data.Vect
vecUp = Vec2 0,1
vecDown = Vec2 0,(-1)
--etc.
goUp10 = 10 *& vecUp -- '*&' is the scalar multiply operator