【问题标题】:Monadic if else如果是单子的话
【发布时间】:2018-03-29 20:00:49
【问题描述】:

我是 Haskell 的新手,想生成一个 Arbitrary 树。
所以我的第一个想法是创建一个任意的布尔值,如果它为真则返回一个空树,否则创建一个非空树:

instance (Arbitrary a) => Arbitrary (BinaryTree a)
  arbitrary = do
    createNonEmpty <- arbitrary
    if createNonEmpty 
      then return Nil
      else generateNonEmptyTree

但是这种创建 bool 并为此使用它的模式似乎有点奇怪,而且感觉应该有一种更惯用的方式。
标准库中是否已经存在某种“单子 if”,我可以像这样使用

arbitrary = ifM arbitrary (return Nil) (generateNonEmptyTree)

或者还有什么最惯用的方法来解决这个问题?

【问题讨论】:

  • ifM 确实是一个非常流行的想法,存在于许多“通用”库中,check out Stackage
  • 如果我理解正确,这些是非标准库?
  • 与 .NET 或 Java 或其他类似的生态系统不同,大多数 Haskell 库是“非标准的”。唯一的“标准”可能是 Prelude。在某种程度上,可以认为 Stack 是 Haskell 的“标准”,从这个意义上说,所有这些库实际上都是“标准”的,因为它们都包含在 Stack 快照中。
  • @ThomasCook 不,他的意思是 Arbitrary 我相信来自 非标准 QuickCheck。
  • @FyodorSoikin (1) “与 .NET 或 Java 或其他类似的生态系统不同,大多数 Haskell 库都是‘非标准的’”——确实;标准库与非标准库在 Haskell 实践中并不真正相关。 (2) “在某种程度上,可以认为 Stack 是 Haskell 的‘标准’,从这个意义上说,所有这些库实际上都是‘标准’”——即使有警告,我觉得这也是很牵强。从设计上讲,堆叠太大且没有凝聚力,无法以这种方式看到。此外,它(和 Stack)的采用还不够接近普遍性。

标签: haskell monads


【解决方案1】:

特别是对于 QuickCheck,我会使用 oneof

arbitrary = oneof [return Nil, generateNonEmptyTree]

它基本上完成了您在问题中提出的建议(生成一次性值,然后立即使用它):

oneof :: [Gen a] -> Gen a
oneof [] = error "QuickCheck.oneof used with empty list"
oneof gs = choose (0,length gs - 1) >>= (gs !!)

但由于它是一个库函数,这意味着您不必查看您自己的代码中的一次性值。

【讨论】:

    【解决方案2】:

    我对“使用一次绑定”的一般解决方案是-XLambdaCase

    instance (Arbitrary a) => Arbitrary (BinaryTree a)
      arbitrary = arbitrary >>= \case
        True  -> return Nil
        False -> generateNonEmptyTree
    

    或者,你可以使用类似的东西

    bool :: a -> a -> Bool -> a
    bool f _ False = f
    bool _ t True = t
    

    Bool 相当于 eitherfoldr

    instance (Arbitrary a) => Arbitrary (BinaryTree a)
      arbitrary = bool generateNonEmptyTree (return Nil) =<< arbitrary
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-20
      • 2015-04-07
      • 1970-01-01
      • 1970-01-01
      • 2015-11-17
      • 1970-01-01
      • 2011-09-11
      • 2011-07-20
      相关资源
      最近更新 更多