简体   繁体   English

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

[英]How to properly type optional arguments in haskell

I am trying to write a function that recursively determines the amount of steps needed to resolve the collatz conjecture.我正在尝试编写一个函数,该函数递归地确定解决 collat​​z 猜想所需的步骤数量。 I want the function input to be just the starting input number for the series, with an additional counter variable to be added in the first iteration.我希望函数输入只是系列的起始输入编号,并在第一次迭代中添加一个额外的计数器变量。 The JS equivalent would look like: 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)
}

My Haskell code:我的哈斯克尔代码:

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

But the compiler complains但是编译器抱怨

    • 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
  |                   ^^^^^^^^^^

What is the error in my thinking?我的想法有什么错误?

Don't pollute your function's public API with an extra parameter used only for an implementation detail.不要使用仅用于实现细节的额外参数污染函数的公共 API。 Instead, have your public 1-argument function delegate to a private 2-argument function.相反,将您的公共 1 参数函数委托给私有 2 参数函数。 Often an internal function like this is named go , if you can't think of a better name.如果你想不出更好的名字,像这样的内部函数通常被命名为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

I've also made some other improvements:我还做了一些其他改进:

  1. There's no reason to return a Maybe Integer when you never return Nothing .当你从不返回Nothing时,没有理由返回一个Maybe Integer
  2. I swapped the parameter order ( count first) to make partial application of go and next more convenient.我交换了参数顺序(先count ),使部分应用gonext更方便。
  3. Instead of defining your helper variable next as an integer, I've defined it as a partial application of go , so that your two cases only have to repeat next , rather than go (...) next .我没有将辅助变量next定义为整数,而是将其定义为go的部分应用,因此您的两种情况只需重复next ,而不是go (...) next

The function as a whole would also be much simpler if you didn't do the counting yourself at all.如果您根本不自己进行计数,整个函数也会简单得多。 I don't suggest this immediately, since it's fairly unrecognizable compared to your initial attempt, but an experienced Haskeller would write this more like:我不会立即建议这样做,因为与您最初的尝试相比,它相当难以辨认,但有经验的 Haskeller 会更像这样写:

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

The error in your thinking is that Maybe is not a keyword, nor is it some kind of a hint to a compiler.您认为的错误在于, Maybe不是关键字,也不是对编译器的某种暗示。 It is a type constructor.它是一个类型构造函数。 Maybe Integer is a concrete, specific data type. Maybe Integer是一种具体的、特定的数据类型。 It is used to represent the general concept of optionality, but by concrete values (of this type).它用于表示可选性的一般概念,但由具体值(这种类型)表示。

Each value in Haskell has one type. Haskell 中的每个值都有一种类型。 It can't be either Int or () .它不能是Int() Haskell's sum types are tagged unions . Haskell 的 sum 类型被标记为unions

A type is a type -- one type.类型是一种类型——一种类型。 Values of type Maybe Int are written either Just i (where i :: Int , ie i has type Int ) or Nothing , according to the data type definition for Maybe ,根据Nothing的数据类型定义, Maybe Maybe Int类型的值可以写为Just i (其中i :: Int ,即i类型为Int )或Nothing

data Maybe a  =  Nothing
              |  Just a 

Conceptually, Maybe Int is the same as Either () Int -- it does represent maybe an Int or nothing , () :从概念上讲, Maybe IntEither () Int相同——它确实表示可能是Int或没有()

data Either a b  =  Left a
                 |  Right b

so Just i is just like Right i , and Nothing is like Left () .所以Just i就像Right iNothing就像Left ()

But not i or () on their own -- they must be tagged with the appropriate data constructor, here Right or Left .但不是i()本身——它们必须用适当的数据构造函数标记,这里是RightLeft

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

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