Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >编写递归模板haskell函数

编写递归模板haskell函数
EN

Stack Overflow用户
提问于 2014-01-14 14:20:10
回答 2查看 407关注 0票数 4

是否可以将递归TH函数转换为将编译的等效形式?以下定义不起作用,因为要编译fact,首先必须编译fact

代码语言:javascript
运行
AI代码解释
复制
fact :: ExpQ -> ExpQ
fact n = [|
    case $n of 
      1 -> 1
      _ -> $n * $(fact [| $n - 1 |]) |]

这个简单的例子很容易解决(fact n = [| product [ 1.. $n] |]),但是在一般情况下,如果不能将给定的函数重写为循环,那么可以定义递归的TH函数吗?有没有一个例子可以这样做呢?

为了向未来的读者澄清:这个问题是专门关于编写递归的TH函数的,而不是关于‘我如何拼接阶乘函数’。

我的问题的答案非常简单:

代码语言:javascript
运行
AI代码解释
复制
{-# LANGUAGE TemplateHaskell #-}

import Control.Monad.Fix (fix)
import Language.Haskell.TH

fact = [| \f x -> $([| 
     case x of 
       1 -> 1 
       _ -> f $([| x - 1 |]) * x  |]) |]

factorial = [| fix $fact |]

可以编译fact,因为它不再是递归的,而[| fix $fact |]是在以后编译的,因此不再有无限的递归定义。

此版本的fact看起来与原始版本略有不同,但您可以编写与旧版本完全相同的新fact,并在以后进行转换:

代码语言:javascript
运行
AI代码解释
复制
fact' recurse n = [|
        case $n of 
          1 -> 1
          _ -> $n * $(recurse [| $n - 1 |]) |]

fact = [| \x -> $((\f -> [| \x -> $(fact (\x -> [| $f $x |]) [| x |]) |]) [| x |]) |]
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-01-14 14:27:21

代码的根本问题不是它是递归的,而是它没有终止。n参数对fact的作用越来越大,因为[| $n - 1 ]|是一个表达式树,表示应用于n1(-)操作。

任何不终止的拼接都会以同样的方式挂起编译器,例如,在拼接时,下面的行为就像您的fact一样:

代码语言:javascript
运行
AI代码解释
复制
broken :: ExpQ -> ExpQ
broken n = return $ LitE (IntegerL (fromIntegral (length [1..])))

保证递归从下到下的递归函数保证终止,并对适当的输入工作良好:

代码语言:javascript
运行
AI代码解释
复制
fact1 :: ExpQ -> ExpQ
fact1 n = do
    nBody <- n
    case nBody of
      LitE (IntegerL 1) -> [|1|]
      LitE (IntegerL nInt) | nInt > 1 ->
          let nMinusOne = return $ LitE (IntegerL (nInt-1))
          in [| $n * $(fact1 nMinusOne) |]

但是,当然,如果输入不是适当的整数文字,它就会失败。

您还可以将递归转移到运行时,这样就不用使用更大的表达式树来进行递归调用,而是使用运行时计算和收缩Int

代码语言:javascript
运行
AI代码解释
复制
fact2 :: ExpQ -> ExpQ
fact2 n =
   [|
    let factImpl n =
         case n of
          1 -> 1
          _ -> n * factImpl (n-1)
    in factImpl $n
   |]

当然,在这段代码中,我们没有对n的结构进行任何分析。但是,我们可以将它与fact1放在一起,以获得在某些情况下执行的编译时版本,并将其他版本推迟到运行时:

代码语言:javascript
运行
AI代码解释
复制
fact3 :: ExpQ -> ExpQ
fact3 n = do
    nBody <- n
    case nBody of
      LitE (IntegerL 1) -> [|1|]
      LitE (IntegerL nInt) ->
          let nMinusOne = return $ LitE (IntegerL (nInt-1))
          in [| $n * $(fact3 nMinusOne) |]
      _ -> [|
            let factImpl n =
                  case n of
                    1 -> 1
                    _ -> n * factImpl (n-1)
            in factImpl $n
           |]

最终,在真正的代码中,您将需要应用这些技术的一些组合--确保编译时递归终止,并以某种方式将剩余的任何情况推迟到运行时评估。

票数 7
EN

Stack Overflow用户

发布于 2014-01-14 15:35:50

是的,您可以使用以下方法:

代码语言:javascript
运行
AI代码解释
复制
fact :: Int -> ExpQ
fact 0 = [| 1 |]
fact n = [| $(lift n) * $(fact $ n - 1) |]

liftLanguage.Haskell.TH.Lift中的一个函数,它将基本的haskell值转换为模板haskell值(如IntExpQ)。

请注意,您不需要生成案例代码,因为您知道编译时的编号。上述宏将扩展到一系列乘法。$(fact 4)将扩展到4*3*2*1

请注意,在本例中,您可以做得更好。模板haskell表达式在编译时运行,因此模板haskell fact函数可以只返回它表示的文字值。例如$(fact 4)可以返回24 (而不是4*3*2*1)。这可以通过以下代码来完成:

代码语言:javascript
运行
AI代码解释
复制
fact2 :: Int -> ExpQ
fact2 n = lift (product [1..n])
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21125220

复制
相关文章
函数curry化(Haskell Curry)
当一个函数fn有多个参数时,可以先传入一部分参数,生成一个中继函数nextFn,然后在nextFn当中再传入剩下的参数。(一步curry化)
elson
2020/01/02
1.3K0
Python 算法基础篇:递归函数的编写和调用
递归是一种重要的编程技巧,通过在函数内部调用自身来解决问题。递归函数的编写和调用在算法中起着关键作用。本篇博客将详细解释递归函数的概念,展示递归函数的编写和调用过程,并通过实例代码演示递归在解决问题中的应用。
小蓝枣
2023/07/24
3800
Haskell
这门语言在数学模型上有着很深的优势,虽然它有很多特性,让人很难接受,随着学习的深入,你才会发现这会多么有趣。
icepy
2019/06/24
9070
Haskell doctest
一定要注意格式 第一行很重要,-- |这行没有就不是一个 test。 可以对比 >>> 的个数 和 terminal里的 Examples 个数确认是否自己的所有 test 都测试了
莫听穿林
2022/05/20
3250
Haskell doctest
热爱函数式的你,句句纯正的 Haskell【函数篇】
Haskell 值与函数是统一的,函数只是需要其他参数输入的值。如果定义的是函数,那么这个函数的行为在运行过程中也是不会改变的,对于某一个特定的输入返回的结果总是确定的,这样的函数为纯函数。
掘金安东尼
2022/09/19
3700
递归函数[通俗易懂]
当然,你可以尝试会发生什么结果,理论上会永远运行下去,但实际操作时发现不一会儿程序就报错了,因为每次调用函数都会用掉一点内存,在足够多的函数调用发生后,空间几乎被占满,程序就会报错。
全栈程序员站长
2022/09/07
7450
递归函数[通俗易懂]
递归函数
这里使用的是命名函数表达式的方法实现递归,将这个函数赋值给 factorial 。这样即使在使用过程中对变量进行修改,也不会影响已赋值的递归函数进行调用,保证了代码的安全性。这种方式在严格模式和非严格模式下都适用。
就只是小茗
2018/12/12
7950
递归函数
递归就是一个函数在它的函数体内调用它自身。执行递归函数将反复调用其自身,每调用一次就进入新的一层。递归函数必须有结束条件。
大忽悠爱学习
2021/03/04
7280
函数递归
如果一个函数在内部调用自身本身,则该函数就是递归函数 递归优缺点   优点:使用递归函数的优点是逻辑简单清晰      理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰   缺点:过深的调用会导致栈溢出 栈溢出   使用递归函数需要注意防止栈溢出   在计算机中,函数调用是通过栈(stack)这种数据结构实现的   每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧   由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出 尾递归   解决递归调用栈溢出的方法是通过尾递归优化   事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的
py3study
2020/01/15
9720
函数模板 ## 函数模板
template <>void Swap<job>(job&, job&); //or template <>void Swap(job&, job&);
Alan_1
2023/04/30
2.2K0
递归、动态规划程序模板
递归代码模板 public int recur (int level, int param){ // 1 终止条件 if (level > maxindex){ return xxx; } //2 处理当前层 中的数据 process(level,param); //3 去到下一层递归 recur (level+1 , param); //4 可能 :如果恢复当前参数的状态,用的少 } 分治的模板 1 终止条件 2 拆分子
猎户星座1
2020/07/01
6260
python递归函数讲解_Python递归函数实例讲解
本文实例讲述了python二分查找算法的递归实现方法.分享给大家供大家参考,具体如下: 这里先提供一段二分查找的代码: def binarySearch(alist, item): first = 0 last = len(alist)-1 found = False while first<=last and not found: midpoint = (first + last)//2 if alist[midpoint] == item: found = True else: if ite
全栈程序员站长
2022/11/17
3.5K0
python递归函数讲解_Python递归函数实例讲解
Haskell Platform安装
不懂了,明天写
云深无际
2020/11/03
1.1K0
Haskell Platform安装
haskell 求助
findBonding :: Eq a => (a -> a -> Bool) -> [a] -> Maybe [(a,a)]
用户6797589
2019/12/02
5670
尾递归函数
Kotlin 支持一种称为尾递归的函数式编程风格。 这允许一些通常用循环写的算法改用递归函数来写,而无堆栈溢出的风险。 当一个函数用 tailrec 修饰符标记并满足所需的形式时,编译器会优化该递归,留下一个快速而高效的基于循环的版本:
阿超
2022/10/31
7480
javascript递归函数
当你把这个函数拿到浏览器上运行的时候,你会发现内存溢出了,为什么呢?因为这个递归函数没有停止处理或运算的出口,因此 这个递归函数就演变为一个死循环。
用户6167509
2019/09/04
7840
优化函数递归
递归是指函数/过程/子程序在运行过程序中直接或间接调用自身而产生的重入现象。在计算机编程里,递归指的是一个过程:函数不断引用自身,直到引用的对象已知。使用递归解决问题,思路清晰,代码少。但是在 Python 中,使用递归会消耗很大的空间,可能还会产生大量的重复的计算。所以我们应该想办法消除递归,下面我以斐波那契序列为例讲解几种消除递归的方法。
不可言诉的深渊
2019/08/13
1.2K0
Python 递归函数
递归函数 在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。 举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * ... * n,用函数 fact(n)表示,可以看出: fact(n) = n! = 1 * 2 * 3 * ... * (n-1) * n = (n-1)! * n = fact(n-1) * n 所以,fact(n)可以表示为 n * fact(n-1),只有n=1时需要特殊处理。 于是,fact(n)用递归的方式写出来就是:
bear_fish
2018/09/20
1.2K0
Python 递归函数
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
全栈程序员站长
2022/09/07
1.4K0
Python 递归函数
Python函数递归
1.己知有一个数列:f(0) = 1,f(1) = 4,f(n + 2) = 2*f(n+ 1) +f(n),其中 n 是大于 0 的整数,求 f(10) 的值。 找出f(n): f(0) = 1,f(1) = 4, f(n + 2) = 2*f(n + 1) +f(n) 当n=1时: f(3)=2*f(2)+f(1) 当n=2时: n=2 f(4)=2*f(3)+f(2) 当n=3时: n=3 f(5)=2*f(4)+f(3) 当n=4时: n=4 f(6)=2*f(5)+f(4) ...... f(n)
织幻妖
2021/07/04
9370
Python函数递归

相似问题

用haskell编写递归inRange函数

22

如何在haskell中编写递归函数

21

如何编写这个递归的Haskell函数?

11

在模板Haskell中定义递归函数

18

如何编写可变模板递归函数?

63
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档