此问题基于11th代码任务的出现。它基本上是河流拼图的一般版本,你可以上下楼层,每一步携带一两件物品。目标是将所有物品调到4楼 使用A *搜索解决这个问题非常简单,但找到相邻状态有点烦人。
当解决这个谜题时,我刚刚为当前楼层的所有项目创建了蒙版,然后使用列表monad生成组合 - 缓慢而笨拙,但它有效。我认为使用镜头会有一个优雅的解决方案。
一个简单的解决方案可以使用一个函数,该函数返回将单个项目从楼层x移动到楼层y的所有选项。有没有办法使用镜头获得将功能应用于单个元素的所有组合?即f 1 2 [(1, 0), (1, 2)] = [[(2, 0) (1, 2)], [(1, 0), (2, 2)]]
为了便于参考,这是迄今为止我能想到的最好的,略微简化:
import Control.Lens
import Data.List (sort)
import Data.Set (fromList, Set)
type GenFloor = Int
type ChipFloor = Int
type State = [(GenFloor, ChipFloor)]
neighborStates :: Int -> State -> Set State
neighborStates currentFloor state = finalize $ createStatesTowards =<< [pred, succ]
where
createStatesTowards direction = traverseOf (traverse . both) (moveTowards direction) state
moveTowards direction i
| i == currentFloor = [direction i, i]
| otherwise = [i]
finalize = fromList . map sort . filter valid
valid = (&&) <$> validCarry <*> validFloors
validCarry = (`elem` [1..2]) . carryCount
carryCount = length . filter (uncurry (/=)) . zip state
validFloors = allOf (traverse . each) (`elem` [1..4])
答案 0 :(得分:1)
一个简单的解决方案可以使用一个函数,该函数返回将单个项目从楼层x移动到楼层y的所有选项。有没有办法使用镜头获得将功能应用于单个元素的所有组合?即
f 1 2 [(1, 0), (1, 2)] = [[(2, 0) (1, 2)], [(1, 0), (2, 2)]]
holesOf
可以做到这一点。从文档中引用相关的简化签名:
holesOf :: Traversal' s a -> s -> [Pretext' (->) a s]
给定遍历,holesOf
将生成一个上下文列表,重点关注遍历所针对的每个元素。来自Control.Comonad.Store
的peeks
可用于从每个上下文修改聚焦目标并重新创建周围结构:
import Control.Lens
import Control.Comonad.Store
-- allMoves :: Int -> Int -> State -> [State]
allMoves :: (Traversable t, Eq a) => a -> a -> t (a, b) -> [t (a, b)]
allMoves src dst its = peeks (changeFloor src dst) <$> holesOf traverse its
where
-- changeFloor :: Int -> Int -> (Int, Int) -> (Int, Int)
changeFloor src dst = over both (\x -> if x == src then dst else x)
GHCi> allMoves 1 2 [(1,0),(1,2)]
[[(2,0),(1,2)],[(1,0),(2,2)]]