您可以学习C语言中的函数式编程吗?[关闭]


9

作为此处评论讨论的结果,我想知道您是否可以学习C语言中的函数式编程?


30
关于是否可以的问题,您不应该
R. Martinho Fernandes

27
绝对!第一步是编写Lisp解释器;-)
Ferruccio

如果我做对了,问题应该是关于用伪代码学习各种概念,而 根本没有任何真正的语言。
SK-logic

@ SK-logic:坦率地说,很少有程序员学会纯粹使用伪代码进行编程,大多数程序员不得不弄脏手,并且从编译器/解释器收到的错误消息也使他们的脸蒙上斑点。
2011年

4
@sbi,当编译器完全没有错误消息时,一些最好的程序员已经学会了编程。打孔卡上的编码要求您对自己正在做的事情有一些了解,然后才有机会运行代码。不用说,功能编程的基础早在建造第一台计算机之前就已建立。
SK-logic

Answers:


21

显然,您可以使用C 进行函数式编程。从理论上讲,您还可以学习使用C进行函数式编程的原理,但是这种语言并不容易。

我假设您至少有一些OOP背景知识;如果这样做,您应该意识到OOP可以在C语言中完成,包括多态性,getter / setter,可见性规则等,但是这样做非常痛苦,而且您需要在内部了解OOP和C语言,拿出来。与FP几乎相同。

应该做的是首先学习一种函数式编程语言(其中大多数具有令人惊讶的简单语法规则;不是使它们难以学习的语法),然后让您新获得的智慧影响您编写C的方式。


根据要求,您可以从FP中学习一些内容,然后将其应用到C,C ++或Java中:

  • 避免状态,尤其是共享的可变状态
  • 欣赏纯功能
  • 赞赏懒惰的评价
  • 能够用动词而不是名词建模
  • 能够递归和迭代地解决问题
  • 使用高阶函数
  • 将功能视为另一种价值,可以传递并与其他价值结合
  • 使用一流的功能替代基于对象的多态性

1
您能否提供C / C ++ / Java程序员如何从LISP智慧中受益的具体示例。从理论上讲这很有意义,但是我正在寻找具体的东西。
工作

@工作,有什么具体的好处?它扩大了他们的思想,也许使他们认为过度可变的状态是一件坏事,您还能要求什么?
dan_waterworth

因此,我可以由此得出结论,尽管可以用C语言学习(某些)FP,但这样做毫无意义吗?
2011年

@Job:多年前,我在学习LISP的时候就学过FP。大约十年后,我将FP应用于模板元编程中。
2011年

1
@sbi,我最喜欢的学习新语言和新概念的方式是通过实施它们。C是用于实现编译器的相当不错的语言。所以,是的,你可以学习FP与C
SK-逻辑

11

可以破解C提供一些功能概念:

这个StackOverflow问题将告诉您更多信息。但是,尽管似乎可以用C进行函数式编程(或其中的很大一部分),但是黑客和编译器扩展以及任何并非学习概念的最佳方法。

要真正学习函数式编程,最好的选择就是著名的函数式编程语言之一,例如Lisp及其方言(ClojureScheme),ErlangHaskell。这些工具中的任何一种都是在功能性编程思想中起作用的完美工具。 如果您具有.Net背景,F#也是一个不错的选择,但是它是一种多范式语言,而不是严格的功能编程语言。


正如tdammers在评论中指出的:

实际上,LISP,clojure和scheme也是多范式。Haskell虽然纯净且默认是惰性的,但它也允许在单子上下文中进行命令式编程,并且它对并发处理具有广泛的支持。所有这些机制都实现了OOP世界中所收集的大部分智慧,包括封装,继承,单一职责,组合等。它是关于哪种范式构成语言的起点的。

据我所知,Lisp及其方言和Erlang比F#更好,因为它们鼓励在其他范式上进行函数式编程,而tdammers很好地将其称为语言的起点。F#确实包含函数式编程,但是不鼓励它比其他受支持的范例,命令式和oo编程好。


关于您的最后一句话:在那种情况下,我认为F#是更好的选择,因为它不会使您陷入实用的思维定势。在多范式语言中,当您开始锤击螺丝时,您只需切换到螺丝起子即可。当陷入单一范式时,您可能会错过螺丝和钉子之间的区别。
R. Martinho Fernandes

问题是如何学习函数式编程,而不是编程。我假设操作员具有多种范例语言的经验,并且希望以最有效的方式学习函数式编程,在这种情况下,F#是一个不错的选择,但不是一个理想的选择。当您希望学习单一范式时,陷入单一范式显然是最好的方法,因为您不必处理并非如此的所有事物。
yannis,2011年

我认为,学习该范例适合什么模式以及何时不适合某个任务是学习它的一部分,也许是最重要的。
R. Martinho Fernandes

4
实际上,LISP,clojure和scheme也是多范式。Haskell虽然纯净且默认是惰性的,但它也允许在单子上下文中进行命令式编程,并且它对并发处理具有广泛的支持。所有这些机制都实现了OOP世界中所收集的大部分智慧,包括封装,继承,单一职责,组合等。它是关于哪种范式构成语言的起点的。
tdammers,2011年

2
@MartinhoFernandes:有人可能会争辩说,被困在一个单一的模式是最完美的方式来学习时,它是真正的屁股:-)一个痛苦
约尔格W¯¯米塔格


2

TL; DR

函数式编程是关于闭包及其应用的。除非有人能够向您展示C的后代关闭库,否则请忘记使用C学习函数式编程。

什么是函数式编程?

函数式编程的基本概念是闭包(closure)的概念,闭包粗略地说是捕获函数以及变量绑定。除了普遍使用闭包之外,函数式编程还具有其他一些独特的特征,例如使用递归函数和不可变值(两者共同发挥作用)。这些特征比任何其他问题更是文化问题,并且几乎没有任何语言使用它们都没有技术障碍,这就是为什么我在回答中关注闭包:并非每种语言都允许轻松创建闭包。

闭包有用性的三个插图

闭包的典型用法是隐私机制的实现。例如,Javascript代码–在示例中,我选择Javascript是因为它是一种具有所谓“类似于C的语法”的功能语言,并且您的问题表明您熟悉C:

create_counter = function()
{
  var x = 0;
  var counter = function()
  {
    ++x;
    return x;
  };
  return counter;
}

然后用

a = create_counter();
b = create_counter();

我们有两个功能ab计算不相交的集合。该示例的要点是变量x由定义counter闭包的闭包捕获,并且每次获取新的counter闭包is instantiated by the function, it gets its fresh own idea of whatx` 时都将捕获。

闭包的另一种典型用法是定义函数的部分应用程序。假设我们有一个类似于syslog实现功能的报告工具

var log = function(priority, message) {

};

这里的参数priority,并message预计将字符串,第一个是一个"debug""info"等。我们可以这样定义一个日志工厂:

var logWithPriority = function(priority) {
  return function(message) {
    log(priority, message);
  };
};

并使用它来定义我们的日志工具的专用版本:

var debug = logWithPriority("debug");
var info = logWithPriority("info");

这非常有用,因为for与其像这样编写容易出错的-loop,

for(i = 0; i < journal.length; ++i) {
   log("info", journal[i]);
}

我们可以写出更清洁,更短和更简单(没有i,那好):

journal.forEach(logWithPriority("info"));

闭包的第三个重要应用领域是懒惰评估的实现–请注意,特殊语言支持可以提供更好的实现。

惰性函数代替执行直接计算,而是返回一个闭包,可以将其称为(或用懒惰的术语“强迫”)执行问题。这样做的动机是将准备计算和执行计算分开。一个实际的例子是正则表达式编译:如果程序在启动时编译了许多正则表达式,则将需要很多时间才能启动。相反,如果我们懒惰地编译正则表达式并根据需要强制使用它们,则程序可以快速启动。当然,这里的正则表达式可以用任何需要大量初始化时间的结构代替。

这是使用闭包实现惰性评估的方法。考虑arrayMax函数的经典实现,该函数返回数组中的max

function arrayMax(array) {
  return array.reduce(function(a, b) {
    return Math.min(a, b);
  };
}

惰性变体为:

function arrayMax(array) {
  var memo = null;
  function actuallyCompute() {
    if(memo === null) {
      memo = array.reduce(function(a, b) {
        return Math.min(a, b);
      });
    }
    return memo;
  }
  return actuallyCompute;
}

返回的值是一个闭包,可以用来计算该值,或者如果已经计算过,则可以再次获取它。

对于这三个示例,我们应该相信闭包及其应用程序是功能编程的核心。

结论

学习函数式编程意味着学习如何使用闭包进行编程。因此,当寻找一种语言来研究函数式编程时,应考虑允许对闭包进行简单操作的语言,尤其是部分函数的应用。相反,闭包不易操作的语言将是错误的选择。


1
感谢您提供的样本。顺便说一句,当您定义var info时,它不是journal.forEach(info)吗?
ejaenv

是的,这将是一个合适的变体!:)
Michael Le BarbierGrünewald2015年

1

我认为您使用的工具会极大地影响您的学习。学习您所使用的编程语言没有提供使用方法的编程概念几乎是不可能的。当然,您总是可以学习一些东西,但是您不能正确地学习它。

但这无论如何都是学术性的,因为正如Martinho在他的评论中所说,即使您可以学习函数式编程,也不尝试这样做,因为在某些语言中这容易得多


1

您不应该学习用C语言编写的函数式编程,而应该使用严格的函数式语言(Haskell,Caml,Erlang等)学习。

如果您不熟悉功能,那么您永远不会真正使用非功能性语言获得它。您更有可能会训练自己去做自己认为是函数式编程的事情,并以错误的方式学习事物。与开始学习正确的方法相比,以正确的方法“重新学习”总是很困难的。

无论如何,对于那些已经知道函数的人来说,我认为用C进行函数是一个很好的练习。因为那个人将了解引擎盖背后的情况-计算机实际上在做什么。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.