2017-02-23 85 views
1

为什么在Perl中下面的递归阶乘失败,即使它在C++和Java中工作?当阶乘递归到零时出错

爪哇:

public static long factorial(int n) { 
    if (n == 0) { 
    return 1; 
    } else { 
    return factorial(n-1) * n; 
    } 
} 

C++:

long factorial(int n) { 
    if (n == 0) 
    return 1; 
    else 
    return n * factorial(n-1); 
} 

但是在Perl这一个失败(use Carp;):

sub factorial { 
    my $n = shift || croak "null value for argument"; 
    return 1 if $n == 0; # base case 
    return $n * factorial($n-1); 
} 

错误信息(第10行是在函数被调用在main(),第16行是第二个return声明):

null value for argument at ./fact1.pl line 14. 
     main::factorial(0) called at ./fact1.pl line 16 
     main::factorial(1) called at ./fact1.pl line 10 

(以下已被编辑感谢马特·雅各布在原始版本指出我遗漏)

原来的版本,没有鲤鱼,但|| return,该错误信息是:

Use of uninitialized value in multiplication (*) at ./fact1.pl line 16. 

这是在没有命令行参数时尝试使脚本静音的结果。 (约uninitialized value无投诉)

基本情况的一个小的修改使得它在Perl的工作太:

sub factorial { 
    my $n = shift || croak "null value for argument"; 
    return 1 if $n == 1; # base case 
    return $n * factorial($n-1); 
} 

(当然,在与处理0(零)输入废除的代价。 )


吸取的教训

  1. 问题是在不同的位置,我想
    • Perl代码已经在部分错误,它试图这样做比C++或Java代码片断(一个非常粗糙的方法)
    • 不同的错误更多消息应该给我一个提示
  2. 以下可帮助我的定位问题:
    • 全部删除后shift;
    • 记得0(零)表示false
  3. Logical Defined-Or存在在Perl,因为5.10版本
  4. 至于正确的错误处理,this page提供了一个很好的概述。
+1

为什么你不使用相同的if/else for Perl? – toolic

+0

回复“为什么不一样if/else”:java来自一个课程示例。 (所以大括号)。 C++,我在Perl之后的测试失败了。 (但我喜欢用简单表达式的大括号)。 在Perl中,等价的“无大括号”解决方案是当条件子句最后到达时。 – Tibor

+1

您在使用'croak'之前关于错误消息的陈述与您的问题中的代码不匹配。如果你调用'factorial()','$ n'将是'undef',并且警告将会是“数字eq中未初始化的$ n”。 (但'undef'会隐式地转换为0,并且该函数仍然会返回1.)真正的问题是:为什么要用一个undefined/null值调用函数,并期望它在Perl中工作时用其他语言工作! –

回答

6

对于什么是真实的,Perl有一些非正统的规则。

尤其是,在布尔表达式中使用时,数字0被认为是错误的。所以,当你说这

my $n = shift || croak "null value for argument"; 

如果shift拉0作为参数,则它被看作是假的,这意味着||运营商需要评估其第二个操作数。

幸运的是,Perl的作者预计这种用例,所以他们定义了一个替代或运算符。鉴于||检查第一个值是否为真,//检查是否定义了第一个值。

my $n = shift // croak "null value for argument"; 

这会你打算什么,除非有$n是文字值undef的可能性。在这种情况下,您需要一些更复杂的内省来检查参数是否存在,但为了这个阶乘函数的目的,这应该就足够了。

+1

你可能会补充说''''和'||'具有比'or'更高的绑定,所以实际上'$ n = shift || croak'实际上将'croak'的返回值赋给'$ n'。由于'croak'是一个'die',没有返回值,因为它永远不会返回,但这仍然很重要。 – simbabque

+2

@simbabque,你在警告我们,Perl实际上正在做我们想要的事情。使用'或'时,分配不必要地发生,我们通过检查分配的结果来间接检查我们想要检查的内容。当使用'||'或'//'时,需要检查的实际内容被检查,并且分配*不会像'或'那样不必要地发生。这实际上是理想的情况,而不是要警告的事情! – ikegami

+0

在我使用的教程和教材中似乎并没有这个操作符,所以我做了一些历史搜索。在Perl 5.10中引入了_defined-or_运算符(//),这是我在第一次找到它的书(Learning Perl,5th Edition)中调用的。 – Tibor