将Haskell代码转换为Python或伪代码

时间:2016-04-30 14:08:42

标签: python haskell

我正在研究an algorithm。但我对作者提供的haskell代码不是很清楚,所以我需要你们这些人。救命。 我认为这些代码可以分为两部分。

> type LFT = (Integer, Integer, Integer, Integer)
>
> extr :: LFT -> Integer -> Rational
> extr (q,r,s,t) x = ((fromInteger q) * x + (fromInteger r)) / ((fromInteger s) * x + (fromInteger t))
>
> unit :: LFT
> unit = (1,0,0,1)
>
> comp :: LFT -> LFT -> LFT
> comp (q,r,s,t) (u,v,w,x) = (q*u+r*w,q*v+r*x,s*u+t*w,s*v+t*x)

在这里,很明显,一个名为LFT的类型(可能是Python中的元组)和三个函数extr unit comp被定义。但是,下一部分让我困惑很多:

> pi = stream next safe prod cons init lfts where
>   init = unit
>   lfts = [(k, 4*k+2, 0, 2*k+1) | k<-[1..]]
>   next z = floor (extr z 3)
>   safe z n = (n == floor (extr z 4))
>   prod z n = comp (10, -10*n, 0, 1) z
>   cons z z’ = comp z z’

我相信lfts是一个生成器,但是我无法理解循环在这段代码中是如何执行的,而且我对Haskell知之甚少。你能帮我把这个转换成Python或伪代码吗?

2 个答案:

答案 0 :(得分:3)

首先,lfts是一个无限列表。您可以使用itertools.count编写非常相似的代码:

from itertools import count

# count(1) is "equivalent2 to [1..]
lfts = ((k, 4*k+2, 0, 2*k+1) for k in count(1))

现在代码的重要一点是对stream的调用,这是一个“执行循环”的函数。该功能在文章中定义为:

> stream :: (b->c) -> (b->c->Bool) -> (b->c->b) -> (b->a->b) ->
>           b -> [a] -> [c]
> stream next safe prod cons z (x:xs)
>   = if   safe z y
>     then y : stream next safe prod cons (prod z y) (x:xs)
>     else stream next safe prod cons (cons z x) xs
>       where y = next z

stream的想法是:

  • 最后一个参数(x:xs)是一个(无限)输入列表(类型为[a]
  • 一对一参数(z)是某种形式的(类型为b
  • 其他四个参数是操纵输入和状态的函数:

    • next函数采用状态并生成输出(y)。
    • safe函数检查是否应在输出中添加y
    • prod函数生成新状态
    • cons添加到输出时调用y函数,并用于根据输入值x生成新状态

您可以重现以下功能:

import itertools as it

def stream(nxt, safe, prod, cons, state, inputs):
    x = next(inputs)   # obtain x
    # xs = inputs
    y = nxt(state)
    if safe(state, y):
        yield y
        inputs = it.chain([x], inputs)   # put x back in the input
        yield from stream(nxt, safe, prod, cons, prod(state, y), inputs)
    else:
        yield from stream(nxt, safe, prod, cons, cons(state, x), inputs)

所以基本上它的作用是它从某个状态开始产生一些值。当它达到某个条件时,它会消耗一个输入值来产生一个新的状态并产生更多的值,当它再次停止时,它将使用另一个输入值再次产生一个新的状态等。无限无限。

请注意,上面的实现会有非常差的性能。最好避免在python中递归,所以我会使用:

def stream(nxt, safe, prod, cons, state, inputs):
    while True:
        x = next(inputs)   # obtain x
        # xs = inputs
        y = nxt(state)
        if safe(state, y):
            yield y
            inputs = it.chain([x], inputs)   # put x back in the input
            state = prod(state, y)
        else:
            state = cons(state, x)

答案 1 :(得分:2)

lfts确实是一个(懒惰)生成器,或多或少等同于:

def lfts () :
    k = 1
    while True :
        yield (k,4*k+2,0,2*k+1)
        k += 1

这是因为[1..]是从1开始的递增整数的无限列表。现在k <- [1..]表示每次k选择列表中的下一个值,并yield列表推导左侧的内容。

因此,它是一个生成无限列表的生成器,因此您不能简单地在其上调用list()len()

您还可以使用itertools中的https://stackoverflow.com/a/32460138/4495081来制作oneliner:

((k,4*k+2,0,2*k+1) for k in itertools.count(1))

然后,您可以使用count来获取前五个元素:

>>> list(itertools.islice(((k,4*k+2,0,2*k+1) for k in itertools.count(1)), 0, 5))
[(1, 6, 0, 3), (2, 10, 0, 5), (3, 14, 0, 7), (4, 18, 0, 9), (5, 22, 0, 11)]

由于生成器可以生成元素直到时间结束,因此您可以轻松地获取任意数量的元素(超过5个,但显然也是20个选项)。