Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
函数式编程:如何将用python编写的斐波那契实现转换为haskell_Python_Haskell - Fatal编程技术网

函数式编程:如何将用python编写的斐波那契实现转换为haskell

函数式编程:如何将用python编写的斐波那契实现转换为haskell,python,haskell,Python,Haskell,因此,对于这个任务,我必须将python转换为haskell。我必须在haskell中实现的代码是使用自顶向下迭代方法生成斐波那契序列的代码。问题是我在haskell还是个新手,我不太知道如何解决这个问题 我在haskell中创建了一个while循环和一个函数 Python代码: def fib_top_down_iter_with_cache(n, trace=False): fibDict = {1:1, 2: 1} (inp, stack) = (['fib', n], [

因此,对于这个任务,我必须将python转换为haskell。我必须在haskell中实现的代码是使用自顶向下迭代方法生成斐波那契序列的代码。问题是我在haskell还是个新手,我不太知道如何解决这个问题

我在haskell中创建了一个while循环和一个函数

Python代码:

def fib_top_down_iter_with_cache(n, trace=False):
    fibDict = {1:1, 2: 1}
    (inp, stack) = (['fib', n], [])
    fib_top_down_iter_with_cache.function_calls = 0
    while inp:
        if trace: 
            print(fibDict, inp, stack)
        (inp, token) = (inp[:-1], inp[-1])
        if isinstance(token, int):
            stack = [token] + stack
        elif token == 'fib':
            (n, stack) = (stack[0], stack[1:])
            fib_top_down_iter_with_cache.function_calls += 1
            if n in fibDict:
                inp = inp + [fibDict[n]]
            else:
                inp = inp + ['cache', n, '+', 'fib', n - 1, 'fib', n - 2]
        elif token == '+':
            (n1, n2, stack) = (stack[0], stack[1], stack[2:])
            inp = inp + [n1 + n2]
        elif token == 'cache':
            (n1, n2, stack) = (stack[0], stack[1], stack[1:])
            fibDict[n1] = n2
        else:
            raise Exception()
    return stack[0]
我在haskell的尝试:

while :: state -> (state -> Bool) -> (state -> state) -> (state -> result) -> result
while state eval bodyFn extractRes
    | eval state = while (bodyFn state) eval bodyFn extractRes
    | otherwise = extractRes state

data Input
    = Word String | Num Integer
    deriving (Eq, Show)

word :: Input -> String
word (Word x) = x

value :: Input -> Integer
value (Num x) = x

fibTopDownIterWithCache :: Integer -> Integer
fibTopDownIterWithCache n = fibTopDownIterWithCache []
fibTopDownIterWithCache n cache = while ([Word "fib", Num n], [])
                                        (-- Just don't know how to implement the rest)
因此,缓存必须实现为Data.Map数据类型,并且我必须将缓存作为函数的属性附加(我想我已经完成了)。然后我必须将缓存作为附加参数传递

期望值只是第n个斐波那契值

回想起来,我有点尴尬,因为你很清楚地问到了“自上而下迭代”技术,我对此一无所知。哦,我希望这在某种程度上仍然有用

python代码中的大量工作是使用显式堆栈和while循环来模拟递归。在Haskell中,我们只使用正则递归;不会出现堆栈溢出或类似情况1。当然,斐波那契的递归定义是:

fib::Int->Integer
fib 0=0
fib 1=1
fib n=fib(n-1)+fib(n-2)
它非常小。我们所要做的就是给它添加缓存

显式缓存传递 如果我们试图保持接近原始代码(但又不去模拟递归),我们应该将缓存作为参数传递。也就是说,我们的函数将获取一个缓存,并返回一个更新的缓存

type Cache=Map.Map Int Integer
fib::Int->Cache->(整数,缓存)
fib 0缓存=(0,缓存)
fib 1缓存=(1,缓存)
fib n缓存=
让(a,cache')=fib(n-1)缓存在
... -- 留作练习
--既然我们撒了两次谎,记得通过考试
--*已更新*缓存,而不是原始缓存,用于第二次调用
我确实推荐这种做法,即使在下一节中它将过时

离题

事实证明,我们的
缓存
传递fib的模式正是
状态
monad捕获的模式。所以我们可以把fib写成

fib::Int->状态缓存整数
实际上,
状态
的定义基本上是:

状态sa=s->(a,s)
模一些
newtype
无意义。如果我们在
状态缓存整数中替换,我们会发现

fib::Int->状态缓存整数
::Int->Cache->(整数,缓存)
就像我们的原创!如果你做了这个练习,你基本上知道monad的
状态是什么

纯回忆录 传递缓存是很好的,但我们可以做得更好。如果仍然能够轻松地查看原始
fib
定义的核心逻辑,而不必担心状态的线程化,那岂不是太好了?这就是页面上的示例所讨论的内容:

memoized_fib::Int->Integer
记忆化的_fib=(映射fib[0..]!!)
其中fib 0=0
fib 1=1
fib n=记忆化的fib(n-2)+记忆化的fib(n-1)
这种语言有一点倾向:我们在
memorized\u fib
where
子句中隐藏了“real”
fib
定义,而“real”定义会调用它的父级
memorized\u fib
。但最初的功能仍然非常清晰,没有太多细节

第一行包含一个示例,以防您以前没有看到它。它只是语法糖,与记忆技术无关

其工作方式是第一行创建一个(单个,program-global2)无限列表

[fib 0,fib 1,fib 2,fib 3,fib 4,fib 5,…]
因为懒惰,没有对其进行评估(这需要很长时间,不是吗?)。然后,当我们需要知道一个特定的斐波那契数时,比如说
4
,我们将索引到列表中,从而只计算该元素。这将更新相应的thunk(延迟挂起的计算)

[fib 0,fib 1,fib 2,fib 3,3,fib 5,…]
因此,如果我们再次访问第四个元素,它已经被评估过了。当然,我们要求使用fibonacci数的原因之一是计算其他fibonacci数(因为
fib
递归到
memonized_fib
),因此现在中间结果也将缓存在该列表中,因此计算速度会呈指数级加快

索引到列表中是O(n),因为Haskell列表本质上是链表。所以备忘表有O(n)查找;我们可以通过使用trie做得更好。这就是我的库所提供的,还有一些其他库,比如对基本相同的东西有着稍微不同的观点的库。使用这些库,您可以对O(logn)备忘录表使用相同的纯备忘录技术

这就是为什么26行Python变成了5行Haskell。哈斯金快乐


1堆栈溢出可能发生在Haskell中,但这不是因为递归太深,通常是因为您太懒了,需要强制执行某些操作`

2如果您想了解更多信息,技术术语是或CAF

3个还是4个太多了

fibs=0:scanl(+)1个fibs


我认为这个问题的答案确实显示了Haskell的一个亮点:领域特定语言的设计和实现。在Python使用字符串和反射的地方,我们将使用代数数据类型来表示语言中的命令,从而使实现干净且易于检查完整性(此外,与Python版本不同,如果您在其中一个命令中键入了错误,编译器将让您知道!)

Fi
import Data.Map (Map)
import qualified Data.Map as M

data Command = Push Integer | Plus | Fib | Cache  deriving (Eq, Ord, Read, Show)
data Machine = Machine
    { program :: [Command]
    , cache   :: Map Integer Integer
    , stack   :: [Integer]
    } 
    deriving (Eq, Ord, Read, Show)
step :: Machine -> Machine
step m = case program m of
    []   -> m
    c:cs -> case (c, stack m) of
        (Push n, ns)   -> m { program = cs, stack = n:ns }
        (Plus, a:b:ns) -> m { program = cs, stack = a+b:ns }
        (Fib, n:ns) -> case M.lookup n (cache m) of
            Just fibn -> m { program = cs, stack = fibn:ns }
            Nothing   -> m { stack = ns, program
                = Push (n-2) : Fib
                : Push (n-1) : Fib
                : Plus
                : Push n : Cache
                : cs
                }
        (Cache, n:ns@(fibn:_)) -> m
            { program = cs
            , stack = ns
            , cache = M.insert n fibn (cache m)
            }
        _ -> error $ "stack too short for command " ++ show c
interpret :: Machine -> Machine
interpret = until (null . program) step
fibMachine :: Integer -> Machine
fibMachine n = Machine
    { program = [Fib]
    , cache = M.fromList [(1,1), (2,1)]
    , stack = [n]
    }

fib :: Integer -> Integer
fib = head . stack . interpret . fibMachine
> fib 10
55
tracingInterpret :: Machine -> IO Machine
tracingInterpret m = mapM_ print unfinished >> return finished 
    where 
    (unfinished, finished:_) = break (null . program) (iterate step m)
> tracingInterpret (fibMachine 4)
Machine {program = [Fib], cache = fromList [(1,1),(2,1)], stack = [4]}
Machine {program = [Push 2,Fib,Push 3,Fib,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = []}
Machine {program = [Fib,Push 3,Fib,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = [2]}
Machine {program = [Push 3,Fib,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = [1]}
Machine {program = [Fib,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = [3,1]}
Machine {program = [Push 1,Fib,Push 2,Fib,Plus,Push 3,Cache,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = [1]}
Machine {program = [Fib,Push 2,Fib,Plus,Push 3,Cache,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = [1,1]}
Machine {program = [Push 2,Fib,Plus,Push 3,Cache,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = [1,1]}
Machine {program = [Fib,Plus,Push 3,Cache,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = [2,1,1]}
Machine {program = [Plus,Push 3,Cache,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = [1,1,1]}
Machine {program = [Push 3,Cache,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = [2,1]}
Machine {program = [Cache,Plus,Push 4,Cache], cache = fromList [(1,1),(2,1)], stack = [3,2,1]}
Machine {program = [Plus,Push 4,Cache], cache = fromList [(1,1),(2,1),(3,2)], stack = [2,1]}
Machine {program = [Push 4,Cache], cache = fromList [(1,1),(2,1),(3,2)], stack = [3]}
Machine {program = [Cache], cache = fromList [(1,1),(2,1),(3,2)], stack = [4,3]}
Machine {program = [], cache = fromList [(1,1),(2,1),(3,2),(4,3)], stack = [3]}