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

发布于 2024-07-23 11:54:36 字数 549 浏览 5 评论 0原文

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

首先,有 这个问题,以及还有这个

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

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

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

A phrase that I've noticed recently is the concept of "point free" style...

First, there was this question, and also this one.

Then, I discovered here they mention "Another topic that may be worth discussing is the authors' dislike of point free style."

What is "point free" style? Can someone give a concise explanation? Does it have something to do with "automatic" currying?

To get an idea of my level - I've been teaching myself Scheme, and have written a simple Scheme interpreter... I understand what "implicit" currying is, but I don't know any Haskell or ML.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

巷雨优美回忆 2024-07-30 11:54:36

只需查看维基百科文章即可获得定义:

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

Haskell 示例:

常规(显式指定参数):

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

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

 sum = foldr (+) 0

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

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

Just look at the Wikipedia article to get your definition:

Tacit programming (point-free programming) is a programming paradigm in which a function definition does not include information regarding its arguments, using combinators and function composition [...] instead of variables.

Haskell example:

Conventional (you specify the arguments explicitly):

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

Point-free (sum doesn't have any explicit arguments - it's just a fold with + starting with 0):

 sum = foldr (+) 0

Or even simpler: Instead of g(x) = f(x), you could just write g = f.

So yes: It's closely related to currying (or operations like function composition).

一桥轻雨一伞开 2024-07-30 11:54:36

无点风格意味着所定义的函数的参数没有明确提及,该函数是通过函数组合来定义的。

如果您有两个函数,例如

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

Point-free style means that the arguments of the function being defined are not explicitly mentioned, that the function is defined through function composition.

If you have two functions, like

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

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

and if you want to combine these two functions to one that calculates x*x+1, you can define it "point-full" like this:

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

The point-free alternative would be not to talk about the argument x:

f :: a -> a
f = inc . square
來不及說愛妳 2024-07-30 11:54:36

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'

参考

A JavaScript sample:

//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

心是晴朗的。 2024-07-30 11:54:36

点自由风格意味着代码没有明确提及它的参数,即使它们存在并且正在使用。

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

例如:

myTake = take

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

Point free style means that the code doesn't explicitly mention it's arguments, even though they exist and are being used.

This works in Haskell because of the way functions work.

For instance:

myTake = take

returns a function that takes one argument, therefore there is no reason to explicit type the argument unless you just want too.

烟─花易冷 2024-07-30 11:54:36

对此最好的解释是 Haskell wiki (摘录如下),由 < a href="https://stackoverflow.com/q/944446/1814970">Petr 在评论中。

在声明中

fx = x + 1 
  

我们根据函数 f 对任意点 x 的作用来定义函数 f。 将此与无积分版本进行对比:

f = (+ 1) 
  

其中没有提及函数所作用的值。

该点是一个数学点(上面的x),因此是“无点”符号。 如果您需要,该链接提供了更多详细信息。

The best explanation for this is in Haskell wiki (excerpt below), pointed out by Petr in a comment.

In the declaration

f x = x + 1

we define the function f in terms of its action on an arbitrary point x. Contrast this with the points-free version:

f = (+ 1)

where there is no mention of the value on which the function is acting.

The point is a mathematical point (x above), hence "points-free" notation. The link gives more detail, if you need.

很糊涂小朋友 2024-07-30 11:54:36

我无法使 Brunno 提供的 javascript 示例工作,尽管代码清楚地说明了 pointfree 想法(即没有参数)。 因此,我使用 ramda.js 提供另一个示例。

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

如果我使用纯 JS 风格代码,我将像这样编写代码,使用映射和化简函数

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,我仍然使用映射和化简函数。 我

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 个步骤,那么它将成为一个真正的奖励)。

I can't make the javascript sample provided Brunno work, although the code illustrate pointfree idea (i.e. no arguments) clearly. So I use ramda.js to provide another example.

Say I need to find out the longest word in a sentence, given a string "Lorem ipsum dolor sit amet consectetur adipiscing elit" I need output something like { word: 'consectetur', length: 11 }

If I use plain JS style code I will code like this, using a map and a reduce function

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)

With ramda I still use a map & a reduce but I will code like this

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)

I will argue that the gist of my ramda code is the pipe that chains my functions together and it makes my purpose more clearly. No need to create a temporary variable strArray is a bonus (if I have more than 3 steps in the pipe then it will become a real bonus).

信愁 2024-07-30 11:54:36

这是一个没有任何其他库的 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;
    }
  }
}

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

Here is one example in TypeScript without any other library:

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;
    }
  }
}

You can see point-free style is more "fluent" and easier to read.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文