Haskell 不管输入是递归的,函数本身只调用一次吗?

Haskell 不管输入是递归的,函数本身只调用一次吗?,haskell,recursion,Haskell,Recursion,考虑一个函数,它执行一个操作,并且不管输入是什么,最多调用一次自身(不超过一个“递归”级别)。 这个函数被认为是递归的吗 例如: join1 :: [Maybe a] -> [a] join1 [Just x] = [x] join1 [Nothing] = [] join1 (x) = concat (map join1 (map (\k->[k]) x)) 在这种情况下,呼叫时: Join1[没有,只有2,只有3,…] 将对每个元素调用join1函数,立即输入终止条件是,它是递

考虑一个函数,它执行一个操作,并且不管输入是什么,最多调用一次自身(不超过一个“递归”级别)。 这个函数被认为是递归的吗

例如:

join1 :: [Maybe a] -> [a]
join1 [Just x] = [x]
join1 [Nothing] = []
join1 (x) = concat (map join1 (map (\k->[k]) x))
在这种情况下,呼叫时: Join1[没有,只有2,只有3,…]
将对每个元素调用join1函数,立即输入终止条件

是,它是递归的。运行时调用的数量与运行时递归深度无关。如果
x
出现在
expression
中,则定义
x=expression
称为递归定义,即我们现在定义的
x

简要地说,“递归性”是定义的句法属性,不考虑运行时行为。

作为一个愚蠢的例子,这个定义是递归的,即使它从不在运行时调用自己。即使它可以简单地简化为非递归的

identity :: a -> a
identity x = (\_ -> x) identity

您低估了函数的有效输入。如果你把它叫做:

join1 [Nothing, Just [Just 2, Nothing, Just 3], Just [Nothing, Just 4]]
join1 [Nothing]
或者换一种方式,如果你这样称呼它:

join1 [Nothing, Just [Just 2, Nothing, Just 3], Just [Nothing, Just 4]]
join1 [Nothing]

非无限递归函数调用的深度总是有限的。对于任何特定的输入,调用自身一次甚至零次都是完全有效的。事实上,如果您不允许使用零递归调用进行输入,它不会终止。

无论输入如何,它应该如何调用自己一次?如果是这种情况,调用将导致第二次调用,第三次调用,等等。答案必须是“是”,因为任何调用自身的函数,即使(与大多数递归函数一样)只对某些输入进行调用,根据定义都是递归的。然而,正如@WillemVanOnsem所指出的,你的处境是一个逻辑上的矛盾。它并不总是称自己为。@adrien:那么输入肯定很重要。此外,这实际上会多次调用
join1
:列表中的每个项调用一次。一个迭代次数不超过两次的循环是否仍被视为循环?我不明白为什么不。我认为真正的问题是,这个函数应该是递归的,还是以一种非递归的方式编写它更好?当然,它会在运行时以非惰性的方式调用自己(无限)language@RobinZigmond说得迂腐一点,我认为即使是在JavaScript中,上面的方法也可以,因为我从来没有调用过这个函数。但是,总的来说,你是完全正确的,因为我可以定义
identity x=(\\\->x)(identity x)
,这在严格的语言中肯定不起作用。看来你是对的,我只是在浏览器控制台(Chrome)
函数identity(x){return(function(y){return x;})(identity)}
而且该函数确实与标识函数一样工作良好。我猜现代JS引擎意识到,既然内部函数不使用其参数,那么就没有必要对其求值-但我认为“严格求值”的“严格”定义意味着所有表达式都需要在遇到时立即求值,这确实会导致这个函数无限递归。你是对的,用
identity(x)
替换
identity
确实会导致“无限”递归(实际上是一个调用堆栈错误)——我想这意味着我不太了解JS的评估策略,因为如果引擎真的发现不需要对参数求值,那么这也不会导致递归。哦,不管怎样,我看到了区别,
identity
只是一个函数值,谈论“求值”是没有意义的。当您实际调用该函数时,这就是强制求值的原因。(很抱歉遗漏了显而易见的内容,您甚至通过强调“通话”一词明确表示了这一点)