用空参数调用函数的术语
Term to call a function with empty parameter
在函数式编程中,调用不接受参数的函数的正确名称是什么?例如:
// 2 types of execute functions
type Function =
| UnitFunction of (unit -> unit)
| OperandFunction of (unit16 -> unit)
let nop () =
()
let shiftRegisterA operand =
registers.A <- registers.A <<< operand
nop
可以叫UnitFunction
,shiftRegisterA
可以叫OperandFunction
吗?
你说的一般是函数的arity。因此,没有参数的函数将被称为空函数,而具有单个参数的函数将被称为一元函数。
虽然您仍然可以在 F# 函数的上下文中使用此术语并得到普遍理解,但这并不完全准确。 F# 函数总是采用单个参数和 return 单个值,因为函数默认是柯里化的。
在您的例子中,nop
的类型为 unit -> unit
。这意味着它接受一个 unit
类型的参数(该类型的唯一 ()
值),并且 return 是 unit
类型的结果。不过,这仍然使它成为一元函数。
在汇编编程中,调用函数是将参数压入堆栈(或寄存器,具体取决于调用约定)然后跳转到函数地址的过程。
在Java、C#、C等更高级的语言中,这个过程或多或少对我们是隐藏的。但是,当我们调用不带参数的函数时,我们会看到它的痕迹,即 void
.
在 F#、haskell 等函数式编程语言中,函数的概念更接近于从单一输入产生答案的数学函数。
要了解 F# 中的所有函数都接受单个输入,让我们看一下以下函数:
// val f: (int*int) -> int
let f (x,y) = x + y
f
接受一对整数并产生一个整数。从 f
的角度来看,该对是解构以产生答案的单个值。
// val g: int -> int -> int
let g x y = x + y
这显然看起来像是一个函数,它接受两个整数来生成一个整数,但如果我们稍微重写一下,我们就会发现情况并非如此:
// val h: int -> int -> int
let h x = fun y -> x + y
h
等同于 g
但这里我们看到 h
实际上采用单个整数生成一个函数,该函数接受一个整数来生成一个整数。
签名中的 ->
是右结合的,我们添加括号我们可以更清楚地看到 g
和 h
实际上采用单个输入。
// val g: int -> (int -> int)
let g x y = x + y
// val h: int -> (int -> int)
let h x = fun y -> x + y
let gx = g 1 2
let gy = (g 1) 2
let hx = h 1 2
let hy = (h 1) 2
在我看来,F# 中的函数比 C#/Java 中的函数具有更高的抽象级别,因为 C#/Java 函数在概念上比 F# 函数更接近汇编语言函数是。
此外,如果每个函数都需要一个参数,那么对于不接受任何参数的函数来说意义不大。
但是这个函数呢?
// val i: unit -> int
let i () = 3
它不接受没有参数产生3吗?不,它接受单位值 ()
,这只是 unit
.
类型的值
在 Haskell 中,他们有一个接受 void
并产生答案的函数的名称:
absurd :: Void -> a
一个值也许可以看作是一个不带参数的函数,但我不是范畴论专家。
返回示例代码:
type Function =
| UnitFunction of (unit -> unit)
| OperandFunction of (unit16 -> unit)
函数式方法是这样的:
type Abstraction =
| Concrete of obj
| Function of Abstraction -> Abstraction
即 Abstraction
是值或函数。
查看代码,它似乎模拟了一些看起来接近汇编语言的东西,所以在这种情况下,可以将函数视为推送参数并跳转到一个地址。
type Function =
| VoidFunction of (unit -> unit)
| UnaryFunction of (unit16 -> unit)
| BinaryFunction of (unit16 -> unit16 -> unit)
希望这很有趣。
PS.
似乎 unit
类型是一个小细节,但在我看来,它可以带来很多好处。
- 无需声明。
- 简化泛型编程(
void
情况通常需要特殊情况,考虑 Task<'T>
和 Task
)。
- 允许我们像数学函数一样思考函数,而不是跳转到内存中的地址。
在函数式编程中,调用不接受参数的函数的正确名称是什么?例如:
// 2 types of execute functions
type Function =
| UnitFunction of (unit -> unit)
| OperandFunction of (unit16 -> unit)
let nop () =
()
let shiftRegisterA operand =
registers.A <- registers.A <<< operand
nop
可以叫UnitFunction
,shiftRegisterA
可以叫OperandFunction
吗?
你说的一般是函数的arity。因此,没有参数的函数将被称为空函数,而具有单个参数的函数将被称为一元函数。
虽然您仍然可以在 F# 函数的上下文中使用此术语并得到普遍理解,但这并不完全准确。 F# 函数总是采用单个参数和 return 单个值,因为函数默认是柯里化的。
在您的例子中,nop
的类型为 unit -> unit
。这意味着它接受一个 unit
类型的参数(该类型的唯一 ()
值),并且 return 是 unit
类型的结果。不过,这仍然使它成为一元函数。
在汇编编程中,调用函数是将参数压入堆栈(或寄存器,具体取决于调用约定)然后跳转到函数地址的过程。
在Java、C#、C等更高级的语言中,这个过程或多或少对我们是隐藏的。但是,当我们调用不带参数的函数时,我们会看到它的痕迹,即 void
.
在 F#、haskell 等函数式编程语言中,函数的概念更接近于从单一输入产生答案的数学函数。
要了解 F# 中的所有函数都接受单个输入,让我们看一下以下函数:
// val f: (int*int) -> int
let f (x,y) = x + y
f
接受一对整数并产生一个整数。从 f
的角度来看,该对是解构以产生答案的单个值。
// val g: int -> int -> int
let g x y = x + y
这显然看起来像是一个函数,它接受两个整数来生成一个整数,但如果我们稍微重写一下,我们就会发现情况并非如此:
// val h: int -> int -> int
let h x = fun y -> x + y
h
等同于 g
但这里我们看到 h
实际上采用单个整数生成一个函数,该函数接受一个整数来生成一个整数。
签名中的 ->
是右结合的,我们添加括号我们可以更清楚地看到 g
和 h
实际上采用单个输入。
// val g: int -> (int -> int)
let g x y = x + y
// val h: int -> (int -> int)
let h x = fun y -> x + y
let gx = g 1 2
let gy = (g 1) 2
let hx = h 1 2
let hy = (h 1) 2
在我看来,F# 中的函数比 C#/Java 中的函数具有更高的抽象级别,因为 C#/Java 函数在概念上比 F# 函数更接近汇编语言函数是。
此外,如果每个函数都需要一个参数,那么对于不接受任何参数的函数来说意义不大。
但是这个函数呢?
// val i: unit -> int
let i () = 3
它不接受没有参数产生3吗?不,它接受单位值 ()
,这只是 unit
.
在 Haskell 中,他们有一个接受 void
并产生答案的函数的名称:
absurd :: Void -> a
一个值也许可以看作是一个不带参数的函数,但我不是范畴论专家。
返回示例代码:
type Function =
| UnitFunction of (unit -> unit)
| OperandFunction of (unit16 -> unit)
函数式方法是这样的:
type Abstraction =
| Concrete of obj
| Function of Abstraction -> Abstraction
即 Abstraction
是值或函数。
查看代码,它似乎模拟了一些看起来接近汇编语言的东西,所以在这种情况下,可以将函数视为推送参数并跳转到一个地址。
type Function =
| VoidFunction of (unit -> unit)
| UnaryFunction of (unit16 -> unit)
| BinaryFunction of (unit16 -> unit16 -> unit)
希望这很有趣。
PS.
似乎 unit
类型是一个小细节,但在我看来,它可以带来很多好处。
- 无需声明。
- 简化泛型编程(
void
情况通常需要特殊情况,考虑Task<'T>
和Task
)。 - 允许我们像数学函数一样思考函数,而不是跳转到内存中的地址。