如何在 haskell 中正确键入可选参数
How to properly type optional arguments in haskell
我正在尝试编写一个函数来递归地确定解决 collatz 猜想所需的步数。我希望函数输入只是该系列的起始输入编号,并在第一次迭代中添加一个额外的计数器变量。等效的 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)
}
我的Haskell代码:
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
| ^^^^^^^^^^
我的思路哪里出了问题?
不要使用仅用于实现细节的额外参数污染函数的 public API。相反,让您的 public 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
我还做了一些其他的改进:
- 没有理由 return 一个
Maybe Integer
当你从不 return Nothing
.
- 我交换了参数顺序(
count
在前),以便更方便地部分应用 go
和 next
。
- 我没有将您的辅助变量
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的求和类型是tagged unions.
一个类型就是一个类型——一个类型。根据数据类型,Maybe Int
类型的值被写入 Just i
(其中 i :: Int
,即 i
具有类型 Int
)或 Nothing
Maybe
、
的定义
data Maybe a = Nothing
| Just a
从概念上讲,Maybe Int
与 Either () Int
相同——它确实表示 可能是 Int
或什么都没有、()
:
data Either a b = Left a
| Right b
所以 Just i
就像 Right i
,而 Nothing
就像 Left ()
。
但不能单独使用 i
或 ()
-- 它们必须使用适当的数据构造器 标记,此处为 Right
或Left
.
我正在尝试编写一个函数来递归地确定解决 collatz 猜想所需的步数。我希望函数输入只是该系列的起始输入编号,并在第一次迭代中添加一个额外的计数器变量。等效的 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)
}
我的Haskell代码:
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
| ^^^^^^^^^^
我的思路哪里出了问题?
不要使用仅用于实现细节的额外参数污染函数的 public API。相反,让您的 public 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
我还做了一些其他的改进:
- 没有理由 return 一个
Maybe Integer
当你从不 returnNothing
. - 我交换了参数顺序(
count
在前),以便更方便地部分应用go
和next
。 - 我没有将您的辅助变量
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的求和类型是tagged unions.
一个类型就是一个类型——一个类型。根据数据类型,Maybe Int
类型的值被写入 Just i
(其中 i :: Int
,即 i
具有类型 Int
)或 Nothing
Maybe
、
data Maybe a = Nothing
| Just a
从概念上讲,Maybe Int
与 Either () Int
相同——它确实表示 可能是 Int
或什么都没有、()
:
data Either a b = Left a
| Right b
所以 Just i
就像 Right i
,而 Nothing
就像 Left ()
。
但不能单独使用 i
或 ()
-- 它们必须使用适当的数据构造器 标记,此处为 Right
或Left
.