0

我正在尝试通过Learn You A Haskell 来学习 Haskell ......但我很不耐烦,想实现我最喜欢的算法,看看我是否可以。

我正在研究用于循环检测的乌龟/野兔算法(弗洛伊德算法)。

这是我到目前为止的代码:

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.

更改所有出现的IntegertoIntNum不让它变得更好。

我不理解Int. 在本教程中,大多数函数的类型声明始终具有以下形式

function_name :: (Some_Type a) => <stuff involving a and possibly other types>

但是当我替换为(Eq a)或者(Num a)(Int a)得到一个类似的错误(类型应用于太多参数)。

我试着阅读这个,但它不同意教程的符号(例如这些示例中定义的几乎每个函数)。

我一定是严重误解了 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)

并给出这个测试功能(基本上就像维基百科示例中的那个),这是有道理的。如果你开始 ax0 = 2那么序列是2 -> 6 -> 1 -> 3 -> 6...,所以mu是 1 (你必须移动一个元素才能到达序列的开头)和lam3 (序列每三个条目重复一次)。

我想在您可能“重复”之前是否总是将第一点视为老化存在一些问题。

如果有人对此有任何建议,我将不胜感激。特别是,我的cntr构造对我来说似乎不起作用..这是一种计算重复调用次数的方法。我不确定是否有更好/不同的方式不像保存变量的状态。

4

2 回答 2

7

你不能说Integer aInt a。你可能的意思是Integral aIntegral包含作为某种整数的所有类型,包括IntegerInt

之前的东西=>不是类型而是类型类。SomeTypeClass a => a表示“a作为类型类成员的任何类型SomeTypeClass”。

你可以这样做:

function :: Int -> String

Int这是一个接受 a并返回 a的函数String。你也可以这样做:

function :: Integer -> String

Integer这是一个接受 a并返回 a的函数String。你也可以这样做:

function :: Integral i => i -> String

这是一个函数,它接受一个Int,或一个Integer,或任何其他类似整数的类型并返回一个String


关于你的第二个问题,你的猜测是正确的。你可以做

mu :: (Eq a, Integral b) => (a -> a) -> a -> a -> b -> (b, a)

您评论的问题:

1. 如果你想确保某个东西的类型是多个类型类的成员,你会怎么做?

你可以做类似的事情

function :: (Show a, Integral a) => a -> String

这将限制a为既是Show和的成员的任何类型Integral

2. 假设您只想将某些参数的类型限制在 TypeClass 中,而您希望其他参数属于特定类型?

然后,您只需将其他参数写为特定类型。你可以做

function :: (Integral a) -> a -> Int -> String

它接受任何类似整数的 type a,然后是 anInt并返回 a String

于 2013-08-14T21:41:58.063 回答
1
于 2013-08-14T22:06:23.920 回答