生成将所有功能应用于带镜头的单个元素的所有方法

时间:2017-02-05 17:03:26

标签: haskell lens

此问题基于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])

1 个答案:

答案 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.Storepeeks可用于从每个上下文修改聚焦目标并重新创建周围结构:

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)]]