【发布时间】:2013-08-14 21:38:05
【问题描述】:
我正在尝试使用 Learn You A Haskell... 学习 Haskell,但我很不耐烦,想实现我最喜欢的算法,看看是否可以。
我正在研究用于循环检测的乌龟/野兔算法 (Floyd's algorithm)。
这是我目前的代码:
idx :: (Eq a) => (a -> a) -> a -> a -> a
idx f tortoise hare
| (f tortoise) == (f (f hare)) = (f f hare)
| otherwise = (idx f) (f tortoise) (f f hare)
mu :: (Eq a) => (a -> a) -> a -> a -> Integer -> (Integer, a)
mu f tortoise hare cntr
| (f tortoise) == (f hare) = (cntr+1, f tortoise)
| otherwise = (mu f) (f tortoise) (f hare) (cntr+1)
lam :: (Eq a) => (a -> a) -> a -> a -> Integer -> Integer
lam f tortoise hare cntr
| tortoise == hare = cntr+1
| otherwise = (lam f) tortoise (f hare) (cntr+1)
floyd :: (Eq a) => (a -> a) -> a -> (Integer, Integer)
floyd f x0 =
let z = (idx f) x0 x0
(y1, t) = (mu f) x0 z 0
y2 = (lam f) t (f t) 0
in (y1, y2)
tester :: (Integer a) => a -> a
tester a
| a == 0 = 2
| a == 2 = 6
| a == 6 = 1
| a == 1 = 3
| a == 3 = 6
| a == 4 = 0
| a == 5 = 1
| otherwise = error "Input must be between 0 and 6"
(floyd tester) 0
这试图将逻辑分解为三个步骤。首先获取 f_idx == f_{2*idx} 的索引,然后从头开始移动以获取参数 mu(从第一个元素到循环开始的距离),然后移动直到遇到重复(循环的长度) .
floyd 函数是我将这些组合在一起的巧妙尝试。
除了这有点不起作用之外,我在加载模块时也遇到了问题,我不知道为什么:
Prelude> :load M:\papers\programming\floyds.hs
[1 of 1] Compiling Main ( M:\papers\programming\floyds.hs, interpreted )
M:\papers\programming\floyds.hs:23:12:
`Integer' is applied to too many type arguments
In the type signature for `tester': tester :: Integer a => a -> a
Failed, modules loaded: none.
将所有出现的Integer 更改为Int 或Num 并没有变得更好。
我不理解Int 的误用。在本教程中,大多数函数的类型声明始终具有以下形式
function_name :: (Some_Type a) => <stuff involving a and possibly other types>
但是当我将(Eq a) 替换为(Num a) 或(Int a) 时,我收到了类似的错误(类型应用于太多参数)。
我试过reading this,但它与教程的符号不一致(例如almost every function defined in these examples)。
我一定是严重误解了 Types 与 TypeClasses,但这正是我认为我确实理解的,从而导致我在上面的代码中进行类型声明。
后续可能是:在函数类型声明中有多个 TypeClass 的语法是什么?比如:
mu :: (Eq a, Int b) => (a -> a) -> a -> a -> b -> (b, a)
(但这也给出了编译错误,说 Int 应用于太多参数)。
已添加
清理并根据答案进行更改,下面的代码似乎可以正常工作:
idx :: (Eq a) => (a -> a) -> a -> a -> a
idx f tortoise hare
| (f tortoise) == (f (f hare)) = (f (f hare))
| otherwise = (idx f) (f tortoise) (f (f hare))
mu :: (Eq a) => (a -> a) -> a -> a -> Integer -> (Integer, a)
mu f tortoise hare cntr
| (f tortoise) == (f hare) = (cntr+1, (f tortoise))
| otherwise = (mu f) (f tortoise) (f hare) (cntr+1)
lam :: (Eq a) => (a -> a) -> a -> a -> Integer -> Integer
lam f tortoise hare cntr
| tortoise == hare = cntr+1
| otherwise = (lam f) tortoise (f hare) (cntr+1)
floyd :: (Eq a) => (a -> a) -> a -> (Integer, Integer)
floyd f x0 =
let z = (idx f) x0 x0
(y1, t) = (mu f) x0 z 0
y2 = (lam f) t (f t) 0
in (y1, y2)
tester :: (Integral a) => a -> a
tester a
| a == 0 = 2
| a == 2 = 6
| a == 6 = 1
| a == 1 = 3
| a == 3 = 6
| a == 4 = 0
| a == 5 = 1
| otherwise = error "Input must be between 0 and 6"
然后我明白了
*Main> floyd tester 2
(1,3)
并给出这个测试函数(基本上就像维基百科示例中的那个),这是有道理的。如果您开始x0 = 2,则序列为2 -> 6 -> 1 -> 3 -> 6...,因此mu 为1(您必须移入一个元素才能到达序列的开头),lam 为3(序列每三个条目重复一次)。
我想在你可能“重复”之前是否总是将第一点视为老化存在一些问题。
如果有人对此有任何建议,我将不胜感激。特别是,我的 cntr 构造对我来说似乎不起作用..这是一种计算重复调用次数的方法。我不确定是否有更好/不同的方式不像保存变量的状态。
【问题讨论】:
标签: haskell typeclass type-declaration