繁体   English   中英

如何在haskell中正确键入可选参数

[英]How to properly type optional arguments in haskell

我正在尝试编写一个函数,该函数递归地确定解决 collat​​z 猜想所需的步骤数量。 我希望函数输入只是系列的起始输入编号,并在第一次迭代中添加一个额外的计数器变量。 JS 等效项如下所示:

const collatz = (v, count) => {
  if (!count) return collatz(v,0)
  if (v === 1) return count
  if (v % 2 === 0) return collatz(v/2, count + 1)
  return collatz(v*3+1, count + 1)
}

我的哈斯克尔代码:

module CollatzConjecture (collatz) where
collatz :: Integer -> Maybe Integer -> Maybe Integer
collatz v () = collatz v 0
collatz 1 count = Just count
collatz v count 
  | even v = collatz (div v 2) next
  | otherwise = collatz (v * 3 + 1) next
  where next = count + 1

但是编译器抱怨

    • Couldn't match type ‘Maybe Integer’ with ‘Integer’
      Expected type: Maybe Integer
        Actual type: Maybe (Maybe Integer)
    • In the expression: Just count
      In an equation for ‘collatz’: collatz 1 count = Just count
  |
4 | collatz 1 count = Just count
  |                   ^^^^^^^^^^

我的想法有什么错误?

不要使用仅用于实现细节的额外参数污染函数的公共 API。 相反,将您的公共 1 参数函数委托给私有 2 参数函数。 如果你想不出更好的名字,像这样的内部函数通常被命名为go

collatz :: Integer -> Integer
collatz = go 0
  where go count 1 = count
        go count v | even v = next $ div v 2
                   | otherwise = next $ v * 3 + 1
          where next = go $ count + 1

我还做了一些其他改进:

  1. 当你从不返回Nothing时,没有理由返回一个Maybe Integer
  2. 我交换了参数顺序(先count ),使部分应用gonext更方便。
  3. 我没有将辅助变量next定义为整数,而是将其定义为go的部分应用,因此您的两种情况只需重复next ,而不是go (...) next

如果您根本不自己进行计数,整个函数也会简单得多。 我不会立即建议这样做,因为与您最初的尝试相比,它相当难以辨认,但有经验的 Haskeller 会更像这样写:

collatz :: Integer -> Int
collatz = length . takeWhile (/= 1) . iterate step
  where step n | even n = n `div` 2
               | otherwise = 3 * n + 1

您认为的错误在于, Maybe不是关键字,也不是对编译器的某种暗示。 它是一个类型构造函数。 Maybe Integer是一种具体的、特定的数据类型。 它用于表示可选性的一般概念,但由具体值(这种类型)表示。

Haskell 中的每个值都有一种类型。 它不能是Int() Haskell 的 sum 类型被标记为unions

类型是一种类型——一种类型。 根据Nothing的数据类型定义, Maybe Maybe Int类型的值可以写为Just i (其中i :: Int ,即i类型为Int )或Nothing

data Maybe a  =  Nothing
              |  Just a 

从概念上讲, Maybe IntEither () Int相同——它确实表示可能是Int或没有()

data Either a b  =  Left a
                 |  Right b

所以Just i就像Right iNothing就像Left ()

但不是i()本身——它们必须用适当的数据构造函数标记,这里是RightLeft

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM