我遇到了一些需要列表的情况:
[(-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这样的词,但我不知道在这种情况下要抓哪一个。
答案 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