以下功能显然在两个列表推导之间存在重复,但如何在不增加代码总长度的情况下消除它?我有一种偷偷摸摸的感觉,这里隐藏着一个很好的抽象,但我看不到它......
letterAt :: [Word] -> Int -> Int -> Maybe Char
letterAt wrds x y = listToMaybe $
[wtext !! (x - wx) |
Word wx wy wtext Across <- wrds,
wy == y && x >= wx && x < wx + length wtext] ++
[wtext !! (y - wy) |
Word wx wy wtext Down <- wrds,
wx == x && y >= wy && y < wy + length wtext]
一些背景知识:
该功能取自填字游戏程序。填字游戏表示为[Word],其中
data Word = Word { startX :: Int,
startY :: Int,
text :: String,
sense :: Sense }
data Sense = Across | Down
感觉的词= =在位置(startX,startY)开始并在正x方向继续,而感觉= =向下的那些在正y方向继续。该函数的目的是将位置(x,y)中的字符设置为Just,如果没有字符则为Nothing。
随意指出我在这里犯下的Haskell的任何其他罪行,我刚开始使用这种语言并且仍然试图掌握它!
答案 0 :(得分:4)
以下是您的代码的一些要点:
filter
。 Data.List.find
您的条件看起来是对称的,因此您可以将transform
函数定义为
transform f x y (Word wx wy wtext Across) = f wtext wy wx y x
transform f x y (Word wx wy wtext Down) = f wtext wx wy x y
现在编写代码只需要写入条件
letterAt :: [Word] -> Int -> Int -> Maybe Char
letterAt wrds x y = (transform charValue x y) <$> find (transform condition x y) wrds
where
condition wtext wx wy x y = wx == x && y >= wy && y < wy + length wtext
charValue wtext wx wy x y = wtext !! (y-wy)
答案 1 :(得分:1)
Satvik,谢谢你的回答让我思考正确的方向。我按照你的建议分离了条件和转换函数,然后意识到转换数据而不是函数会更简单,并将所有内容放回列表理解中以便于阅读:
letterAt :: [Word] -> Int -> Int -> Maybe Char
letterAt wrds x y = listToMaybe
[wtext !! x' | Word wx wy wtext sens <- wrds,
let (x', y') = case sens of
Across -> (x - wx, y - wy)
Down -> (y - wy, x - wx),
y' == 0, x' >= 0, x' < length wtext ]
你指出在这种情况下最好使用Data.find ..这是为了可读性还是效率?我猜测因为列表在Haskell中是懒惰的,上面的代码会在列表推导中的第一项被评估后停止,这是对的吗?