如何在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)
}
我的哈斯克尔代码:
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
我还做了一些其他改进:
Maybe Integer当您从不返回时,没有理由返回 aNothing。- 我换参数顺序(
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