Perl的哪些功能使其成为一种函数式编程语言?

时间:2015-06-25 09:27:54

标签: perl functional-programming

受到启发:https://stackoverflow.com/questions/30977789/why-is-c-not-a-functional-programming-language

我找到了:Higher Order Perl

这让我对Perl是一种函数式编程语言的断言感到好奇。现在,我理解函数式编程是一种技术(很像面向对象)。

但是我找到了what makes a functional programming language的列表:

  • 头等舱功能
  • 高阶函数
  • Lexical Closures
  • 模式匹配
  • 单一作业
  • 懒惰评估
  • 垃圾收集
  • 类型推断
  • 尾调用优化
  • 列表理解
  • Monadic effects

现在其中一些我非常熟悉:

例如,垃圾收集是Perl引用计数,并在不再需要时释放内存。

词法闭包甚至是常见问题的一部分:What is a closure? - 这里可能有更好的文章:http://www.perl.com/pub/2002/05/29/closure.html

但是我开始对其中的一些感到有点模糊 - 例如,列表理解 - 我认为这指的是map / grepList::Utilreduce ?)

我有人能帮我填补这里的空白吗? Perl可以轻松完成以上哪些操作(并且有一个简单的例子)并且有哪些例子可以解决它?

2 个答案:

答案 0 :(得分:20)

有用的相关内容:

Perl monks rant about functional programming

Higher Order Perl

C2.com functional programming definitions

First Class functions

在计算机科学中,如果将编程语言视为一等公民,则称其具有一流的功能。具体来说,这意味着该语言支持将函数作为参数传递给其他函数,将它们作为其他函数的值返回,并将它们分配给变量或将它们存储在数据结构中。

所以Perl:

my $print_something = sub { print "Something\n" };

sub do_something {
    my ($function) = @_;
    $function->();
}

do_something($print_something);

判决:本机支持

Higher Order Functions

  

在数学和计算机科学中,高阶函数(也是函数形式,函数或函子)是至少执行以下任一操作的函数:

     
      
  • 将一个或多个函数作为输入

  •   
  • 输出功能

  •   

参考this post on perlmonks

  

在Perl术语中,我们经常将它们称为回调,工厂和返回代码引用的函数(通常是闭包)。

判决:本机支持

Lexical Closures

在perl常见问题解答中,我们对What is a closure?

有疑问
  

闭包是一个计算机科学术语,具有精确但难以解释的含义。通常,闭包在Perl中作为匿名子例程实现,并且在其自己的作用域之外具有对词法变量的持久引用。这些词汇神奇地指的是定义子程序时的变量(深度绑定)。

     

闭包最常用于编程语言中,您可以将函数的返回值本身作为函数,就像在Perl中一样。

在文章中可能会更清楚地解释这一点:Achieving Closure

sub make_hello_printer {
    my $message = "Hello, world!";
    return sub { print $message; }
}

my $print_hello = make_hello_printer();
$print_hello->()

判决:本机支持

Pattern Matching

  

在纯函数式语言和本页面的上下文中,模式匹配是一种调度机制:选择函数的哪个变量是要调用的正确函数。受标准数学符号的启发。

Dispatch表是最接近的近似值 - 基本上是匿名subs或代码refs的散列。

use strict;
use warnings;

sub do_it {
    print join( ":", @_ );
}
my $dispatch = {
    'onething'      => sub { print @_; },
    'another_thing' => \&do_it,
};

$dispatch->{'onething'}->("fish");

因为它是just一个哈希,你也可以添加代码引用和匿名子程序。 (注意 - 与面向对象的编程并不完全不同)

判决:解决方法

Single Assignment

  

在纯函数式语言中不允许更改现有值(例如x:= x + 1)的任何赋值。4在函数式编程中,不鼓励赋值单个赋值,也称为初始化。单一赋值是名称绑定的一个示例,与本文所述的赋值不同之处在于它通常只能创建一次,通常是在创建变量时;不允许后续的重新分配。

我不确定perl是否确实如此。最接近的近似值可能是引用/匿名subs或者constant

判决:不支持

Lazy Evaluation

  

等到最后一刻评估表达式,特别是为了优化可能不使用表达式值的算法。

Examples of lazy evaluation techniques in Perl 5?

再次回到Higher Order Perl(我没有与本书有关,诚实 - 它似乎只是关于这个主题的关键文本之一)。

这里的核心概念似乎是 - 创建一个链接列表'在perl中(使用面向对象的技术),但在你的“结束标记”中嵌入了一个代码引用。评估你是否做得那么远。

判决:解决方法

Garbage Collection

  

" GarbageCollection(GC),也称为自动内存管理,是堆内存的自动回收。"

Perl通过引用计数执行此操作,并在不再引用它们时释放它们。请注意,这可能会影响您在函数式编程时更可能遇到的某些事情(可能!)。

具体 - perldoc perlref

中涵盖的循环引用

判决:原生支持

Type Inference

  

TypeInference是一个程序的分析,用于推断部分或全部表达式的类型,通常在CompileTime

Perl会根据需要隐式地来回转换值。通常情况下这很好用,你不需要弄乱它。有时候你需要强行“咳嗽”。通过进行显式数字或字符串操作来完成该过程。通常,这可以通过添加0或连接空字符串来实现。

您可以使用dualvars

重载标量以执行不同的操作

判决:原生支持

Tail Call Optimization

  

尾部调用优化(或尾部调用合并或尾部调用消除)是TailRecursion的推广:如果例程在返回之前做的最后一件事是调用另一个例程,而不是执行跳转 - 添加 - 堆栈帧紧跟一个pop-stack-frame-and-return-to-caller,只需跳转到第二个例程的开头就可以了,让它重新使用第一个例程的堆栈帧(环境)。

Why is Perl so afraid of "deep recursion"?

它会工作,但如果您的递归深度大于100,它会发出警告。您可以通过添加以下内容来禁用此功能:

no warnings 'recursion';

但显然 - 你需要对递归深度和内存占用略微谨慎。

据我所知,没有任何特定的优化,如果你想以有效的方式做这样的事情,你可能需要(有效地)展开你的递归然后迭代。

  

Perl支持Tailcalls。请参阅goto⊂表示法,或查看Sub::Call::Tail提供的更简洁的语法

判决:本地

List Comprehensions

  

列表推导是许多现代FunctionalProgrammingLanguages的一个特征。根据某些规则,它们为GeneratingElements提供了简洁的表示法?在列表中。   列表理解是SyntacticSugar,用于函数concat,map和filter的应用程序组合

Perl有mapgrepreduce

它还可以应对范围和重复的扩展:

my @letters = ( "a" .. "z" ); 

所以你可以:

my %letters = map { $_ => 1 } ( "A" .. "z" ); 

判决:原生(List::Utils是核心模块)

Monadic effects

......不,这些仍有问题。它要么比我能理解得简单得多,要么复杂得多。

如果有人有更多的东西,请填写或编辑这篇文章或......某事。我对所涉及的一些概念仍然很粗略,所以这篇文章更多的是一个起点。

答案 1 :(得分:4)

非常好的主题,我想写一篇标题为“骆驼功能正常”的文章。让我贡献一些代码。

Perl也支持像

这样的匿名函数
 sub check_config {
    my ( $class, $obj ) = @_;

    my $separator = ' > ';

    # Build message from class namespace.
    my $message = join $separator, ( split '::', $class );

    # Use provided object $obj or
    # create an instance of class with defaults, provided by configuration.
    my $object = $obj || $class->new;

    # Return a Function.
    return sub {
        my $attribute = shift;

        # Compare attribute with configuration,
        # just to ensure it is read from there.
        is $object->config->{$attribute},

            # Call attribute accessor so it is read from config,
            # and validated by type checking.
            $object->$attribute,

            # Build message with attribute.
            join $separator, ( $message, $attribute );
        }
}

sub check_config_attributes {
    my ( $class, $obj ) = @_;

    return sub {
        my $attributes = shift;
        check_config( $class, $obj )->($_) for (@$attributes);
        }
}