Haskell:通过元组从元组列表中删除元素

时间:2020-03-12 20:52:19

标签: list haskell filter tuples

我的代码中有一个结构,其中包括一个包含许多列表的列表,然后这些列表具有表示坐标的元组。这是我的情况:

type Point = (Int, Int)
type ShipPoints = [Point]

removeCoordinatePairFromList :: Point -> [ShipPoints] -> [ShipPoints]
removeCoordinatePairFromList fireCoordinate enemyShips =  (filter (notElem fireCoordinate) enemyShips)

但是,这不符合我的要求。这将删除父列表中找到匹配坐标对的整个子列表。我希望只将与fireCoordinate匹配的一个元组从子列表中删除,而其他所有内容都保持不变。上下文是战舰游戏,ShipPoints类型表示列表中任何类型的舰船坐标。 [ShipPoints]是指一名玩家的所有飞船坐标。

1 个答案:

答案 0 :(得分:1)

您似乎想遍历ShipPoints列表,并从出现的每个Point中删除ShipPoints。可以使用map

removePointFromShipList :: Point -> [ShipPoints] -> [ShipPoints]
removePointFromShipList p lst = map (removePointFromShip p) lst

这使用了辅助功能:

removePointFromShip :: Point -> ShipPoints -> ShipPoints

从特定的Point中删除ShipPoints。可以使用过滤器定义此辅助功能:

removePointFromShip p shp = filter (/= p) shp

我认为上面的函数很简单,并不需要真正进行改进,但是由于Haskell程序员不能独自留下足够好的声音,因此大多数人(包括我)都会尝试重构它。随意忽略此部分,或者只是略过其中而已。

无论如何,许多Haskeller会将removePointFromShip函数移到where子句中,并可能缩短名称:

removePoint :: Point -> [ShipPoints] -> [ShipPoints]
removePoint p lst = map removePoint' lst
  where removePoint' shp = filter (/= p) shp

然后,许多人会意识到,如果您拥有f x = blah blah blah x,则可以将其替换为f = blah blah blah(此过程称为eta归约)。主函数和辅助函数都可以像这样减少eta:

removePoint :: Point -> [ShipPoints] -> [ShipPoints]
removePoint p = map removePoint'
  where removePoint' = filter (/= p)

现在,有了where子句是没有意义的,

removePoint :: Point -> [ShipPoints] -> [ShipPoints]
removePoint p = map (filter (/= p))

这非常好,大多数人会在这里停下来。真正痴呆的人会意识到有机会通过写作将其转变为“无积分”形式:

removePoint :: Point -> [ShipPoints] -> [ShipPoints]
removePoint = map . filter . (/=)

(从技术上讲,这与以前的版本不同,但是可以,只要p /= qq /= p相同就可以了。)现在,它看起来很聪明,但是没人能理解只是通过查看它,所以我们必须添加评论:

-- Remove Point everywhere it appears in [ShipPoints]
removePoint :: Point -> [ShipPoints] -> [ShipPoints]
removePoint = map . filter . (/=)

太棒了!