为什么一元函数不能用于后缀表示法?
Why aren't unary functions usable in postfix notation?
我看到一元函数的后缀相对于前缀表示法的两个主要优点:
- 格式很好,没有嵌套
- 更好地映射了
Input -> Function -> Ouput
思考数据处理的方式。
这是一个例子:
def plus2(v: Int) = v + 2
def double(v: Int) = v * 2
def square(v: Int) = v * v
// prefix
square(
double(
plus2(1)
)
)
// postfix
1.plus2
.double
.square
这可以像在 Java 流 api 中一样使用方法链或其他用户技术进行模拟。但是我不熟悉任何提供 first-class 支持后缀函数应用程序的编程语言。
不首先提供 class 一元函数的后缀表示法支持的编程语言设计原因是什么?支持起来似乎微不足道。
I'm not familiar with any programming language that allows this.
您的示例语法与 method chaining 非常相似。在面向对象的语言中,没有参数的方法实际上是声明该方法的任何类型的一元运算符。所以这是你在 Java 中的计算,具有相同的声明,后缀顺序:
class Example {
final int x;
Example(int x) { this.x = x; }
Example add2() { return new Example(x + 2); }
Example mult2() { return new Example(x * 2); }
Example square() { return new Example(x * x); }
public static void main(String[] args) {
Example result =
new Example(1)
.add2()
.mult2()
.square();
}
}
你当然需要括号()
来称呼它们,但它仍然是后缀顺序。不幸的是,您不能将其调整为使用静态方法而不是实例方法,至少不能像这样滥用 Optional
或 Stream
:
Optional.of(1)
.map(StaticExample::add2)
.map(StaticExample::mult2)
.map(StaticExample::square)
.get()
我想 OOP 语言之所以不能以这种方式使用静态方法变得更容易,是因为使用特殊语法使静态方法优先于实例方法会很奇怪。 OOP 的重点是用实例和多态来做事。
在函数式语言中也可以,使用函数而不是方法:这里是 F#:
let add2 x = x * 2
let double x = x * 2
let square x = x * x
let result =
1
|> add2
|> double
|> square
这里的前向管道运算符 |>
与 Java 中的 .
起着不同的语义作用,但效果是一样的:函数是按后缀顺序编写的。
我猜前向管道运算符只倾向于存在于函数式语言中的主要原因是部分应用程序允许您使用非一元函数编写管道式计算。例如:
nums
|> List.filter isEven
|> List.map square
在这里,List.filter
和 List.map
有两个参数,但是如果你用一个参数调用它们,那么它们 return 是一个一元函数。非函数式语言往往没有部分应用(至少不那么容易),因此前向管道运算符的用处不大。
还有不太知名的concatenative programming paradigm, where everything is naturally done in postfix order, with no extra syntax required. Here's my own toy language named fffff:
(2 +) >!add2
(2 *) >!double
(@ *) >!square
1 add2 double square >result
在这里,即使像>result
这样的赋值也是按后缀顺序完成的。
我看到一元函数的后缀相对于前缀表示法的两个主要优点:
- 格式很好,没有嵌套
- 更好地映射了
Input -> Function -> Ouput
思考数据处理的方式。
这是一个例子:
def plus2(v: Int) = v + 2
def double(v: Int) = v * 2
def square(v: Int) = v * v
// prefix
square(
double(
plus2(1)
)
)
// postfix
1.plus2
.double
.square
这可以像在 Java 流 api 中一样使用方法链或其他用户技术进行模拟。但是我不熟悉任何提供 first-class 支持后缀函数应用程序的编程语言。
不首先提供 class 一元函数的后缀表示法支持的编程语言设计原因是什么?支持起来似乎微不足道。
I'm not familiar with any programming language that allows this.
您的示例语法与 method chaining 非常相似。在面向对象的语言中,没有参数的方法实际上是声明该方法的任何类型的一元运算符。所以这是你在 Java 中的计算,具有相同的声明,后缀顺序:
class Example {
final int x;
Example(int x) { this.x = x; }
Example add2() { return new Example(x + 2); }
Example mult2() { return new Example(x * 2); }
Example square() { return new Example(x * x); }
public static void main(String[] args) {
Example result =
new Example(1)
.add2()
.mult2()
.square();
}
}
你当然需要括号()
来称呼它们,但它仍然是后缀顺序。不幸的是,您不能将其调整为使用静态方法而不是实例方法,至少不能像这样滥用 Optional
或 Stream
:
Optional.of(1)
.map(StaticExample::add2)
.map(StaticExample::mult2)
.map(StaticExample::square)
.get()
我想 OOP 语言之所以不能以这种方式使用静态方法变得更容易,是因为使用特殊语法使静态方法优先于实例方法会很奇怪。 OOP 的重点是用实例和多态来做事。
在函数式语言中也可以,使用函数而不是方法:这里是 F#:
let add2 x = x * 2
let double x = x * 2
let square x = x * x
let result =
1
|> add2
|> double
|> square
这里的前向管道运算符 |>
与 Java 中的 .
起着不同的语义作用,但效果是一样的:函数是按后缀顺序编写的。
我猜前向管道运算符只倾向于存在于函数式语言中的主要原因是部分应用程序允许您使用非一元函数编写管道式计算。例如:
nums
|> List.filter isEven
|> List.map square
在这里,List.filter
和 List.map
有两个参数,但是如果你用一个参数调用它们,那么它们 return 是一个一元函数。非函数式语言往往没有部分应用(至少不那么容易),因此前向管道运算符的用处不大。
还有不太知名的concatenative programming paradigm, where everything is naturally done in postfix order, with no extra syntax required. Here's my own toy language named fffff:
(2 +) >!add2
(2 *) >!double
(@ *) >!square
1 add2 double square >result
在这里,即使像>result
这样的赋值也是按后缀顺序完成的。