其他编程语言的魔术方法
Magic methods on other programming languages
Python有__add__
、__mul__
、__cmp__
等方法(称为magic methods),作为class 方法,并且可以赋予不同的含义以添加 (+
)、乘法 (*
)、比较 (==
)、... class 的两个实例.我的问题是其他语言有类似的方法吗?我熟悉 Java、C++、ruby 和 PHP,但从未遇到过这样的事情。我知道这四个都有一个对应于 __init__
的构造函数方法,但是其他魔术方法呢?
我尝试使用谷歌搜索 "Magic methods in other programming languages" 但没有找到任何相关信息,可能它们在不同语言上的名称不同。
在 C++
operator overloading 中是您要查找的内容。
Java
不支持运算符重载 (Reference)。
C
没有运算符重载 (Reference)。于是,写了一大堆add
、mult
等等函数。通常那些是宏,因为它们可以用于不同的类型。恕我直言,这就是我更喜欢 C++ 的原因。
@Alex reference 很好地概述了运算符覆盖。
一般来说,一种语言中 "magic" 过多是语言设计不良的标志。也许这就是为什么没有很多语言有魔术方法?
这样的魔法创建了一个双class系统:语言设计者可以为语言添加新的魔法方法,但程序员只能使用语言设计大祭司允许的方法到。一般来说,程序员应该可以做到尽可能多的而不需要改变语言规范。
例如,在 Scala 中,+
、-
、*
、/
、==
、!=
、<
、>
、<=
、>=
、::
、|
、&
、||
、&&
, **
, ^
, +=
, -=
, *=
, /=
, 等等,都是合法的标识符。因此,如果您想为自己的对象实现自己的乘法版本,只需编写一个名为 *
的方法即可。这只是一个无聊的旧标准方法,绝对没有什么"magic"。
相反,any 方法可以使用运算符符号调用,即不带点。任何只接受一个参数的方法都可以在运算符符号中不带括号地调用。
这不仅适用于方法。此外,任何具有两个类型参数的类型构造函数都可以在中缀表示法中使用,所以如果我有
class ↔️[A, B]
我可以
class Foo extends (String ↔️ Int)
与
相同
class Foo extends ↔️[String, Int]
好吧……我有点撒谎了:Scala 中有一些语法糖:
如果范围内没有名为 foo
的方法,foo()
将转换为 foo.apply()
。这允许您有效地重载函数调用运算符。
foo.bar = baz
被翻译成 foo.bar_=(baz)
。这允许您有效地重载 属性 赋值。 (这就是您在 Scala 中编写 setter 的方式。)
foo(bar) = baz
被翻译成 foo.update(bar, baz)
。这允许您有效地重载索引分配。 (例如,这就是您在 Scala 中编写数组或字典访问的方式)。
!foo
(和其他几个)被翻译成 foo.unary_!
.
foo += bar
会尝试调用foo
的+=
方法,即等价于foo.+=(bar)
。但是如果失败 and foo
是一个有效的左值,and foo
有一个名为 +
的方法,那么 Scala 也会尝试 foo = foo + bar
而不是
此外,优先级、结合性和固定性在 Scala 中是固定的:它们由方法名称的第一个字符决定。 IE。所有以 *
开头的方法具有相同的优先级,所有以 -
开头的方法具有相同的优先级,依此类推。
Haskell 更进一步:函数和运算符之间没有基本 区别。每个函数都可以用在函数调用符号和运算符符号中。唯一的区别是词法上的:如果函数名由运算符字符组成,那么当我想在函数调用符号中使用它时,我必须将它括在括号中。 OTOH,如果函数名称由字母数字字符组成并且我想在运算符符号中使用它,我需要将它用反引号括起来。所以,以下是等价的:
a + b
(+) a b
a `plus` b
plus a b
对于函数的运算符用法,可以自由定义固定性、结合性、优先级,例如:
infixr 15 <!==!>
在Ruby中有一组预定义的运算符,有相应的方法,例如:
def +(other)
plus(other)
end
Python有__add__
、__mul__
、__cmp__
等方法(称为magic methods),作为class 方法,并且可以赋予不同的含义以添加 (+
)、乘法 (*
)、比较 (==
)、... class 的两个实例.我的问题是其他语言有类似的方法吗?我熟悉 Java、C++、ruby 和 PHP,但从未遇到过这样的事情。我知道这四个都有一个对应于 __init__
的构造函数方法,但是其他魔术方法呢?
我尝试使用谷歌搜索 "Magic methods in other programming languages" 但没有找到任何相关信息,可能它们在不同语言上的名称不同。
在 C++
operator overloading 中是您要查找的内容。
Java
不支持运算符重载 (Reference)。
C
没有运算符重载 (Reference)。于是,写了一大堆add
、mult
等等函数。通常那些是宏,因为它们可以用于不同的类型。恕我直言,这就是我更喜欢 C++ 的原因。
@Alex reference 很好地概述了运算符覆盖。
一般来说,一种语言中 "magic" 过多是语言设计不良的标志。也许这就是为什么没有很多语言有魔术方法?
这样的魔法创建了一个双class系统:语言设计者可以为语言添加新的魔法方法,但程序员只能使用语言设计大祭司允许的方法到。一般来说,程序员应该可以做到尽可能多的而不需要改变语言规范。
例如,在 Scala 中,+
、-
、*
、/
、==
、!=
、<
、>
、<=
、>=
、::
、|
、&
、||
、&&
, **
, ^
, +=
, -=
, *=
, /=
, 等等,都是合法的标识符。因此,如果您想为自己的对象实现自己的乘法版本,只需编写一个名为 *
的方法即可。这只是一个无聊的旧标准方法,绝对没有什么"magic"。
相反,any 方法可以使用运算符符号调用,即不带点。任何只接受一个参数的方法都可以在运算符符号中不带括号地调用。
这不仅适用于方法。此外,任何具有两个类型参数的类型构造函数都可以在中缀表示法中使用,所以如果我有
class ↔️[A, B]
我可以
class Foo extends (String ↔️ Int)
与
相同class Foo extends ↔️[String, Int]
好吧……我有点撒谎了:Scala 中有一些语法糖:
-
如果范围内没有名为
foo()
将转换为foo.apply()
。这允许您有效地重载函数调用运算符。foo.bar = baz
被翻译成foo.bar_=(baz)
。这允许您有效地重载 属性 赋值。 (这就是您在 Scala 中编写 setter 的方式。)foo(bar) = baz
被翻译成foo.update(bar, baz)
。这允许您有效地重载索引分配。 (例如,这就是您在 Scala 中编写数组或字典访问的方式)。!foo
(和其他几个)被翻译成foo.unary_!
.foo += bar
会尝试调用foo
的+=
方法,即等价于foo.+=(bar)
。但是如果失败 andfoo
是一个有效的左值,andfoo
有一个名为+
的方法,那么 Scala 也会尝试foo = foo + bar
而不是
foo
的方法,此外,优先级、结合性和固定性在 Scala 中是固定的:它们由方法名称的第一个字符决定。 IE。所有以 *
开头的方法具有相同的优先级,所有以 -
开头的方法具有相同的优先级,依此类推。
Haskell 更进一步:函数和运算符之间没有基本 区别。每个函数都可以用在函数调用符号和运算符符号中。唯一的区别是词法上的:如果函数名由运算符字符组成,那么当我想在函数调用符号中使用它时,我必须将它括在括号中。 OTOH,如果函数名称由字母数字字符组成并且我想在运算符符号中使用它,我需要将它用反引号括起来。所以,以下是等价的:
a + b
(+) a b
a `plus` b
plus a b
对于函数的运算符用法,可以自由定义固定性、结合性、优先级,例如:
infixr 15 <!==!>
在Ruby中有一组预定义的运算符,有相应的方法,例如:
def +(other)
plus(other)
end