Haskell do记法能否用Scala for推导式实现等价逻辑?
用Scala 3 for推导式实现等价于Haskell do记法的平衡二叉树构建逻辑
先明确核心问题:Haskell的do记法对List Monad的支持允许自然生成结构的列表,但Scala的for推导式要求yield返回单个元素而非列表——但这并不意味着不能用for推导式实现等价逻辑,只要贴合Scala的Monad规则调整写法即可。
原Haskell的典型do记法实现(示例)
假设你的Haskell代码是类似这样的平衡二叉树构建逻辑:
data Tree a = Leaf | Node a (Tree a) (Tree a) deriving Show buildBalanced :: [a] -> [Tree a] buildBalanced [] = [Leaf] buildBalanced xs = do let mid = length xs `div` 2 (left, x:right) = splitAt mid xs leftTree <- buildBalanced left rightTree <- buildBalanced right return $ Node x leftTree rightTree
对应的Scala 3 for推导式实现
首先定义树的枚举类型:
enum Tree[A]: case Leaf case Node(value: A, left: Tree[A], right: Tree[A])
然后用for推导式实现等价逻辑:
def buildBalanced[A](xs: List[A]): List[Tree[A]] = xs match case Nil => List(Tree.Leaf) case _ => val mid = xs.length / 2 val (leftList, x :: rightList) = xs.splitAt(mid) for leftTree <- buildBalanced(leftList) rightTree <- buildBalanced(rightList) yield Tree.Node(x, leftTree, rightTree)
为什么这样能行?
- Scala的for推导式本质是
flatMap和map的语法糖,对于List类型来说,每一步的<-会自动展开列表中的所有元素 buildBalanced(leftList)返回List[Tree[A]],leftTree <- ...会遍历这个列表的每一个子树实例- 同理处理右子树的列表,最终
yield生成单个Node实例,for推导式会自动组合所有左右子树的可能组合,返回完整的List[Tree[A]],和Haskell do记法的逻辑完全一致
对比flatMap实现
如果你的flatMap代码是这样:
def buildBalanced[A](xs: List[A]): List[Tree[A]] = xs match case Nil => List(Tree.Leaf) case _ => val mid = xs.length / 2 val (leftList, x :: rightList) = xs.splitAt(mid) buildBalanced(leftList).flatMap(leftTree => buildBalanced(rightList).map(rightTree => Tree.Node(x, leftTree, rightTree) ) )
那上面的for推导式就是这段代码的完全等价语法糖,结构更接近Haskell的do记法,可读性更强。
注意事项
如果你的原Haskell代码存在多分支返回多个结构的情况,只要确保每一步<-绑定的都是List类型,yield返回单个元素,Scala的for推导式就能自动处理所有组合,和Haskell的行为一致。
内容的提问来源于stack exchange,提问作者Abhijit Sarkar
相关产品推荐
相关产品推荐

