25

维基百科曾经说过*关于鸭子打字

在使用面向对象编程语言的计算机编程中,鸭子类型是一种动态类型,其中对象的当前方法和属性集确定有效语义,而不是从特定类或特定接口的实现继承。

(* 编者注:自发布此问题以来,维基百科文章已被编辑以删除“动态”一词。)

它说的是结构类型

结构类型系统(或基于属性的类型系统)是类型系统的主要类,其中类型兼容性和等价性由类型的结构决定,而不是通过显式声明。

它将结构子类型与鸭子类型进行对比:

[结构系统] 与 ... 鸭子类型形成对比,其中只检查在运行时访问的结构部分的兼容性。

然而,在我看来, duck-typing一词至少直观地包含了结构子类型系统。事实上维基百科说:

概念的名称 [duck-typing] 指的是鸭子测试,归因于 James Whitcomb Riley,它可以表述如下:“当我看到一只鸟儿像鸭子一样走路、像鸭子一样游泳、像鸭子一样嘎嘎叫时,我称那只鸟为鸭子。”

所以我的问题是:为什么我不能将结构子类型称为鸭子类型?是否甚至存在不能被归类为鸭子类型的动态类型语言?

后记:

正如 reddit.com 上一个叫daydreamdrunk的人如此雄辩地说: “如果它像鸭子一样编译并像鸭子一样链接......”

后记

许多答案似乎基本上只是重复我在这里已经引用的内容,而没有解决更深层次的问题,这就是为什么不使用术语鸭子类型来涵盖动态类型和结构子类型?如果您只想谈论鸭子类型而不是结构子类型,那么就称它为:动态成员查找。我的问题是,鸭子打字这个词对我没有任何意义,这只适用于动态语言。

4

8 回答 8

29

C++ 和 D 模板是非动态的鸭子类型的完美示例。肯定是:

键入其中对象的当前方法和属性集确定有效语义,而不是它从特定类或特定接口实现的继承。

您没有明确指定您的类型必须继承的接口来实例化模板。它只需要具有模板定义中使用的所有功能。然而,一切都在编译时得到解决,并编译成原始的、难以理解的十六进制数字。我称之为“编译时鸭子打字”。我从这种思维方式编写了整个库,即隐式模板实例化是编译时的鸭子类型,并认为它是最被低估的功能之一。

于 2009-12-22T18:31:58.383 回答
15

结构类型系统

结构类型系统将一个完整类型与另一个完整类型进行比较,以确定它们是否兼容。对于两个类型AB要兼容,A并且B必须具有相同的结构——也就是说, on和 on的每个方法都必须具有相同的签名。AB

鸭打字

如果它们都可以处理该任务,那么Duck typing 认为两种类型对于手头的任务是等效的。对于两种类型A,并且B要等价于一段代码,要写入一个文件,A并且B两者都必须实现一个write方法。

概括

结构类型系统比较每个方法签名(整个结构)。Duck typing 比较与特定任务相关的方法(与任务相关的结构)。

于 2009-12-22T18:19:25.473 回答
11

鸭子打字意味着如果它适合,就可以了

这适用于动态类型

def foo obj
    obj.quak()
end

或静态类型的编译语言

template <typename T>
void foo(T& obj) {
    obj.quak();
}

关键是,在这两个示例中,都没有关于给定类型的任何信息。仅在使用时(在运行时或编译时!),检查类型,如果满足所有要求,则代码工作。值在其声明点没有明确的类型

与往常一样,结构类型依赖于显式键入您的值 - 不同之处在于具体类型不是由继承标识,而是由其结构标识。

上述示例的结构类型代码(Scala 风格)将是

def foo(obj : { def quak() : Unit }) {
    obj.quak()
}

不要将此与 OCaml 之类的结构类型语言将其与类型推断相结合以防止我们显式定义类型这一事实混淆。

于 2009-12-22T18:59:29.657 回答
6

我不确定它是否真的回答了你的问题,但是......

模板化的 C++ 代码看起来很像鸭子类型,但它是静态的、编译时的、结构化的。

template<typename T>
struct Test
{
    void op(T& t)
    {
        t.set(t.get() + t.alpha() - t.omega(t, t.inverse()));
    }
};
于 2009-12-22T18:05:42.073 回答
5

据我了解,类型推断器等使用结构类型来确定类型信息(想想 Haskell 或 OCaml),而鸭子类型本身并不关心“类型”,只是事物可以处理特定的方法调用/属性访问等(想想respond_to?Ruby 或 Javascript 中的能力检查)。

于 2009-12-22T18:04:38.893 回答
5

某些编程语言中总会有一些示例违反了各种术语的某些定义。例如,ActionScript 支持对技术上非动态的实例进行鸭式编程。

var x:Object = new SomeClass();
if ("begin" in x) {
    x.begin();
}

在这种情况下,我们测试了“x”中的对象实例在调用它之前是否有一个方法“begin”,而不是使用接口。这在 ActionScript 中有效,并且几乎是鸭式类型,即使类 SomeClass() 本身可能不是动态的。

于 2009-12-22T18:10:25.817 回答
4

在某些情况下,动态鸭子类型和类似的静态类型代码(即 C++ 中)表现不同:

template <typename T>
void foo(T& obj) {
    if(obj.isAlive()) {
        obj.quak();
    }
}

在 C++ 中,对象必须同时具有isAlivequak方法才能编译代码;对于动态类型语言中的等价代码,对象只需要有返回true的quak方法。isAlive()我将此解释为结构(结构类型)和行为(鸭子类型)之间的差异。

(然而,我通过从表面上理解维基百科的“鸭式打字必须是动态的”并试图使其有意义来达到这种解释。隐式结构打字是鸭式打字的替代解释也是连贯的。)

于 2014-04-17T16:19:08.030 回答
2

我将“鸭子类型”更多地视为一种编程风格,而“结构类型”是一种类型系统功能。

结构类型是指类型系统表达类型的能力,该类型包括具有某些结构属性的所有值。

鸭子类型是指编写代码,只使用它传递的值的特性,这些特性是手头工作实际需要的,而不施加任何其他约束。

因此,我可以通过将我的“鸭子类型”正式声明为结构类型来使用结构类型以鸭子类型进行编码。但我也可以使用结构类型而不用“做鸭子打字”。例如,如果我通过声明和命名一个公共结构类型然后在任何地方使用它来为一堆相关的函数/方法/过程/谓词/类/任何东西编写接口,那么很可能某些代码单元不需要结构类型的所有特征,因此我不必要地限制其中一些拒绝它们理论上可以正确工作的值。

因此,虽然我可以看到有共同点,但我不认为鸭式类型包含结构类型。在我看来,鸭式打字甚至不能包含结构打字,因为它们不是同一类东西。恕我直言,将动态语言中的鸭子类型视为“隐式的、未经检查的结构类型”是缺失的。Duck typing 是您选择使用或不使用的编码风格,而不仅仅是编程语言的技术特征。

例如,可以isinstance在 Python 中使用检查来伪造 OO 风格的“类或子类”类型约束。还可以检查特定的属性和方法,以伪造结构类型约束(您甚至可以将检查放在外部函数中,从而有效地获得命名的结构类型!)。我会声称这些选项都不是鸭子类型的例证(除非结构类型非常细粒度并且与它们的代码检查保持密切同步)。

于 2014-04-24T01:35:44.310 回答