什么是“无点”风格(在函数式编程中)?

Posted

技术标签:

【中文标题】什么是“无点”风格(在函数式编程中)?【英文标题】:What is "point free" style (in Functional Programming)? 【发布时间】:2010-10-30 23:24:42 【问题描述】:

我最近注意到的一个短语是“无点”风格的概念......

首先,有this的问题,还有also this one。

然后,我发现here 他们提到“另一个可能值得讨论的话题是作者不喜欢无点风格。”

什么是“无点”风格?有人可以给出简明的解释吗?它与“自动”柯里化有关吗?

为了了解我的水平 - 我一直在自学 Scheme,并编写了一个简单的 Scheme 解释器...我了解“隐式”柯里化是什么,但我不知道任何 Haskell 或 ML。

【问题讨论】:

请注意:要了解它为什么称为 pointfree,请访问 HaskellWiki 上的 Pointfree/But pointfree has more points!。 【参考方案1】:

只需查看Wikipedia article 即可获得您的定义:

默认编程(无点编程)是一种编程范式,其中函数定义不包含有关其参数的信息,使用组合子和函数组合 [...] 而不是变量。

Haskell 示例:

常规(您明确指定参数):

sum (x:xs) = x + (sum xs)
sum [] = 0

无点(sum 没有任何显式参数 - 它只是与 + 以 0 开头的折叠):

 sum = foldr (+) 0

或者更简单:你可以写g = f,而不是g(x) = f(x)

所以是的:它与柯里化(或函数组合之类的操作)密切相关。

【讨论】:

啊,我明白了!所以你总是通过组合其他函数而不是声明参数来构建新函数......非常优雅! 我真的不喜欢在编程时为变量/参数想出新名称。这是我喜欢无点风格的一大原因! 跟柯里化有什么关系? @kaleidic:因为没有变量名,你需要组合部分应用的函数。这就是我们所说的柯里化(或者更准确地说,是通过柯里化实现的) 你不是说sum (x:xs) ... 而不是sum sum (x:xs) ... 吗?【参考方案2】:

Point-free 风格意味着被定义函数的参数没有明确提及,函数是通过函数组合来定义的。

如果你有两个函数,比如

square :: a -> a
square x = x*x

inc :: a -> a
inc x = x+1

如果你想将这两个函数组合成一个计算x*x+1的函数,你可以像这样定义它“point-full”:

f :: a -> a
f x = inc (square x)

无意义的替代方案是不谈论论点x

f :: a -> a
f = inc . square

【讨论】:

愚蠢的是,在 Haskell 中,“无点”方式通常看起来更尖(更多句点)。这种烦恼是一个很好的助记符。 (Real World Haskell 对此一书进行了说明。) 关于@Dan 的评论,PointfreeHaskellWiki 页面解释了为什么它被称为 pointfree @Dan:我不认为这很愚蠢,因为 Haskell 点的意思是“那个圆圈运算符”(虽然应该看起来更像°)。但令人困惑的是,尤其是当您不熟悉函数式编程语言时;每本关于 haskell 的介绍书都应该解释 point-free-style。【参考方案3】:

一个 javascript 示例:

//not pointfree cause we receive args
var initials = function(name) 
  return name.split(' ').map(compose(toUpperCase, head)).join('. ');
;

const compose = (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];
const join = m => m.join();

//pointfree
var initials = compose(join('. '), map(compose(toUpperCase, head)), split(' '));

initials("hunter stockton thompson");
// 'H. S. T'

Reference

【讨论】:

链接已更改为github.com/MostlyAdequate/mostly-adequate-guide/blob/master/… 但无法让代码运行【参考方案4】:

无点样式意味着代码没有明确提及它的参数,即使它们存在并且正在使用。

由于函数的工作方式,这在 Haskell 中有效。

例如:

myTake = take

返回一个接受一个参数的函数,因此没有理由显式键入参数,除非你也想要。

【讨论】:

有时,它在 Haskell 98 中不起作用,就像在 myShow = show 中一样。在Haskell wiki 上有更多关于它的信息【参考方案5】:

我无法让 Brunno 提供的 javascript 示例工作,尽管代码清楚地说明了无点的想法(即没有参数)。所以我用ramda.js再举一个例子。

假设我需要找出一个句子中最长的单词,给定一个字符串"Lorem ipsum dolor sit amet consectetur adipiscing elit",我需要输出类似 word: 'consectetur', length: 11 的东西

如果我使用纯 JS 样式代码,我将使用 map 和 reduce 函数编写这样的代码

let str = 'Lorem ipsum dolor sit amet consectetur adipiscing elit'
let strArray = str.split(' ').map((item) => ( word: item, length: item.length ))
let longest = strArray.reduce(
    (max, cur) => (cur.length > max.length ? cur : max), 
    strArray[0])
console.log(longest) 

对于 ramda,我仍然使用 map 和 reduce,但我会像这样编写代码

const R = require('ramda')
let longest = R.pipe(
  R.split(' '),
  R.map((item) => ( word: item, length: item.length )),
  R.reduce((max, cur) => (max.length > cur.length ? max : cur),  length: 0 )
)
let tmp = longest(str)
console.log(tmp)

我会争辩说,我的 ramda 代码的要点是将我的函数链接在一起的管道,它使我的目的更加清晰。不需要创建一个临时变量strArray 是一个奖励(如果我在管道中有超过 3 个步骤,那么它将成为一个真正的奖励)。

【讨论】:

【参考方案6】:

这是一个没有任何其他库的 TypeScript 示例:

interface Transaction 
  amount: number;


class Test 
  public getPositiveNumbers(transactions: Transaction[]) 
    return transactions.filter(this.isPositive);

    //return transactions.filter((transaction: amount: number => transaction.amount > 0));
  

  public getBigNumbers(transactions: Transaction[]) 
    // point-free
    return transactions.filter(this.moreThan(10));

    // not point-free
    // return transactions.filter((transaction: any) => transaction.amount > 10);
  

  private isPositive(transaction: Transaction) 
    return transactions.amount > 0;
  

  private moreThan(amount: number) 
    return (transaction: Transaction) => 
      return transactions.amount > amount;
    
  

您可以看到无点样式更“流畅”且更易于阅读。

【讨论】:

这不是无点风格,这只是 lambda 和命名函数之间的区别。 @kralyk 我想你没抓住重点,this.moreThan(10) 不是一个命名函数,它是一个柯里化函数以及一个隐含的函数(因此没有点)将 transaction 作为它的输入。

以上是关于什么是“无点”风格(在函数式编程中)?的主要内容,如果未能解决你的问题,请参考以下文章

js函数式编程简介

浅谈函数式编程

大前端进击之路:函数式编程

函数式编程介绍

函数响应式编程(FRP)思想-Callback风格

浅谈Java 8的函数式编程