Каковы некоторые лучшие способы записи [(-1, -1), (- 1,0), (- 1,1), (0, -1), (0,1), (1, -1), (1,0), (1,1)] в Хаскелле?
Я столкнулся с несколькими ситуациями, когда мне нужен список:
[(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)] -- no (0,0)
Обратите внимание, что в списке нет (0,0). Я использую кортежи (dx, dy) для поиска вверх, вниз, влево, вправо и по диагонали от координаты.
Каждый раз, когда я пишу его, я чувствую, что должен быть более сжатый и/или более простой способ прочитать его. Я относительно новичок в Haskell, и я фигурирую где-то в сумке с аппликативными /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", что является хорошим явным способом сказать "мы делаем то же самое в два раза", но я не могу принять карту впереди с большой небезопасной лямбдой и двумя именами.
[(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", и у меня есть имя. Это мой любимый пока, но он не чувствует себя прекрасно.
Я думаю, что если бы я понял, как лучше писать, я мог бы глубже понять Функторы/Монады или тому подобное. Я читал о них совсем немного, и я слышал кучу слов, таких как fmap/mconcat/etc, но я не знаю, в какую из них можно было бы справиться в этой ситуации.
Ответы
Ответ 1
На самом деле, я думаю, что лучше всего написать это явно в этом случае. Просто выровняйте его разумно, и никаких вопросов не останется открытым:
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] ]
что, конечно, дороже для вычисления, но это не имеет значения, если вы только определяете этот список в одном месте и повторно используете его со всей вашей программы. Порядок здесь другой, не уверен, что это важно.
Ответ 2
Почему бы просто не использовать понимание списка? Они могут иметь в себе булевую охрану, поэтому исключение (0,0) довольно просто:
[(i,j) | i <- [-1..1], j <- [-1..1], (i,j) /= (0,0)]
Обратите внимание, что как сам Haskell newb, вероятно, более компактный/эффективный способ написать это защитное выражение. Тем не менее, это выполняет свою работу.
Ответ 3
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)]
Ответ 4
Здесь краткая версия, использующая материал из 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
Все, что сказал, я по-прежнему предпочитаю версию со списком или даже буквальным списком с хорошим отступом.
Ответ 5
liftA2 zip (!!0) (!!1) . transpose . tail . replicateM 2 $ [0,1,-1]
(Не то, чтобы я рекомендую это.)
Ответ 6
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))]
Ответ 7
Для упрощения чтения вы можете попробовать:
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