我正在尝试更精通递归方案,因为到目前为止,它们确实有助于将粗糙的显式递归代码转变为较少的尖峰。在实施可能真正令人困惑的显式递归混淆的算法时,我倾向于提供的其他工具之一是Monad Transformers / Mutability。理想情况下,我想对递归方案感到足够的舒适感,以便我可以完全抛弃状态。我仍然可以触及的算法的一个示例是带有alpha beta修剪的minimax。我使用can态和minimax f-algebra( data minimaxf af = mmresult a | mmstate [f] bool
)进行了正常的minimax,但是我不确定如何扩展此操作以进行alpha beta beta修剪。我认为也许我可以使用组织态性,或者也许有一些与ComOnads的自定义解决方案,但是我不知道如何使用任何一种技术来尝试解决方案。
除了具有递归方案的Alpha Beta修剪版本外,您对解决类似问题的任何一般建议都将不胜感激。例如,我很难将递归方案应用于通常以命令式实施的Dijkstra等算法。
I'm trying to get more proficient with recursion schemes as they have so far been really helpful for turning gnarly explicit recursion code into something less spike-y. One of the other tools I tend to reach for when implementing algorithms that can get really confusing with explicit recursion is monad transformers / mutability. Ideally I'd like to get comfortable enough with recursion schemes such that I can ditch statefulness altogether. An example of an algorithm I'd still reach for the transformers for is minimax with alpha beta pruning. I did normal minimax with a catamorphism and minimax f-algebra (data MinimaxF a f = MMResult a | MMState [f] Bool
), but I wasn't sure how I could extend this to do alpha beta pruning. I thought maybe I could use histomorphism, or maybe there was some custom solution with comonads, but I didn't know how to approach trying a solution using either technique.
In addition to a version of alpha beta pruning with recursion schemes any general advice you have about tackling similar problems would be much appreciated. For example I've had trouble applying recursion schemes to algorithms like Dijkstra that usually are implemented in an imperative fashion.
发布评论
评论(1)
alpha-beta可以看作是minimax的实例,其中
min
和max
是使用精心选择的晶格实例化的。 完整的GIST 。我们将游戏表示为树,每个内部节点都是游戏中的位置,等待指定玩家选择移动到子节点,而每片叶子都是带有其得分或价值的最终位置。
minimax
将在以下类定义的任何晶格上工作:lattice
类比ord> ord> ord> ord
:和代码>实例是
lattice
,具有可决定性等效的(eq
)。如果我们可以重新定义ORD
,那么将lattice
作为超类是适当的。但是在这里,newtype必须做:这是minimax。它是通过嵌入
leaf :: a - &gt进行参数化的。 l
最终值为所选晶格。一个玩家最大化嵌入式值,另一个玩家将其最小化。“常规” minimax将游戏的分数直接用作晶格:
对于Alpha-Beta修剪,其想法是我们可以跟踪最佳分数的某些界限,这使我们可以在搜索中缩短搜索。因此,搜索将通过该间隔
(Alpha,beta)
进行参数化。这使我们进入了函数的晶格间隔a - >
:一个间隔可以由
表示(也许是A,也许是A)
,以允许双方无限。但是,我们将使用更好的命名类型来清晰,并且在每一侧都利用不同的ord
实例:我们将要求我们只能构造
修剪f
如果f
满足夹具i(fi)=夹具i(f(bot,top))
,其中夹具
在下面定义。这样一来,f
是一种搜索算法,如果它得知其结果在间隔之外,则可以将其折叠起来,而无需找到确切的结果。函数通过尖端提起形成晶格。而且,当我们仅考虑满足
夹具i(fi)=夹具i(f(bot,top))的功能时>如果
clamp<*> f = clamp<*> g
),则可能是晶格的短路定义。两个功能的
inf
l
和r
,给定间隔i =(alpha,beta)
,首先运行l(Alpha,beta)
获得值vl
。如果
vl< = alpha
,则必须是夹具i vl == alpha ==钳位i(min vl(ri))
,因此我们可以停止并返回vl
而无需查看r
。否则,我们知道最终结果将不超过vl
,因此我们还可以更新传递给r r
。 >。sup
对称定义。因此,我们获得了α-beta作为minimax的实例。一旦定义了上面的晶格,我们只需要一些简单的包装和解开即可。
如果一切顺利,
alphabeta
和minimax
应该具有相同的结果:Alpha-beta can be seen as an instance of minimax, where
min
andmax
are instantiated using a well-chosen lattice. Full gist.We represent games as a tree, where each internal node is a position in the game, waiting for a designated player to pick a move to a child node, and each leaf is a final position with its score, or value.
minimax
will work on any lattice, defined by the following class:The
Lattice
class is more general thanOrd
: andOrd
instance is aLattice
with decidable equality (Eq
). If we could redefineOrd
, then it would be appropriate to addLattice
as a superclass. But here a newtype will have to do:Here's minimax. It is parameterized by an embedding
leaf :: a -> l
of final values to the chosen lattice. One player maximizes the embedded value, the other player minimizes it.The "regular" minimax uses the scores of the game directly as the lattice:
For alpha-beta pruning, the idea is that we can keep track of some bounds on the optimal score, and this allows us to short-circuit the search. So the search is to be parameterized by that interval
(alpha, beta)
. This leads us to a lattice of functionsInterval a -> a
:An interval can be represented by
(Maybe a, Maybe a)
to allow either side to be unbounded. But we shall use better named types for clarity, and also to leverage a differentOrd
instance on each side:We will require that we can only construct
Pruning f
iff
satisfiesclamp i (f i) = clamp i (f (Bot, Top))
, whereclamp
is defined below. That way,f
is a search algorithm which may shortcircuit if it learns that its result lies outside of the interval, without having to find the exact result.Functions form a lattice by pointwise lifting. And when we consider only functions satisfying
clamp i (f i) = clamp i (f (Bot, Top))
and equate them modulo a suitable equivalence relation (Pruning f = Pruning g
ifclamp <*> f = clamp <*> g
), a short-circuiting definition of the lattice becomes possible.The
inf
of two functionsl
andr
, given an intervali = (alpha, beta)
, first runsl (alpha, beta)
to obtain a valuevl
.If
vl <= alpha
, then it must beclamp i vl == alpha == clamp i (min vl (r i))
so we can stop and returnvl
without looking atr
. Otherwise, we runr
, knowing that the final result is not going to be more thanvl
so we can also update the upper bound passed tor
.sup
is defined symmetrically.Thus we obtain alpha-beta as an instance of minimax. Once the lattice above is defined, we only need some simple wrapping and unwrapping.
If all goes well,
alphabeta
andminimax
should have the same result: