用例如Java编写的程序很大程度上依赖于动态调度.如何用Haskell等函数式语言表达这些程序?换句话说,Haskell在"动态调度"下表达这个想法的方式是什么?
从C++/Java/C#背景开始我希望在Swift中看到虚拟方法,但是阅读swift文档我没有提到虚拟方法.
我错过了什么?
我有以下代码:
extern crate futures; // 0.1.24
use futures::Future;
use std::io;
struct Context;
pub trait MyTrait {
fn receive(context: Context) -> Future<Item = (), Error = io::Error>;
}
pub struct MyStruct {
my_trait: MyTrait,
}
Run Code Online (Sandbox Code Playgroud)
当我尝试编译它时,我收到错误消息:
error[E0038]: the trait `MyTrait` cannot be made into an object
--> src/lib.rs:13:5
|
13 | my_trait: MyTrait,
| ^^^^^^^^^^^^^^^^^ the trait `MyTrait` cannot be made into an object
|
= note: method `receive` has no receiver
Run Code Online (Sandbox Code Playgroud)
我想我知道它为什么会发生,但我如何从结构中引用特征呢?可能吗?也许还有其他方法可以实现相同的行为?
我找到了一些关于打开/关闭递归的解释,但我不明白为什么定义包含"递归"这个词,或者它与动态/静态调度的比较.在我发现的解释中,有:
打开递归.大多数语言使用对象和类提供的另一个便利功能是一个方法体通过一个名为
self或在某些语言中调用的特殊变量来调用同一对象的另一个方法的能力this.self的特殊行为是它是后期绑定的,允许在一个类中定义的方法在第一个类的某个子类中调用稍后定义的另一个方法.[ Ralf Hinze ]
......或者在维基百科:
调度语义
this,即对此调用的方法调用是动态调度的,称为开放递归,意味着这些方法可以被派生类或对象覆盖.相比之下,函数的直接命名递归或匿名递归使用闭合递归,具有早期绑定.
我还阅读了StackOverflow问题:什么是开放递归?
但我不明白为什么"递归"这个词用于定义.当然,如果通过执行方法递归调用来使用"开放递归",它可能会导致有趣(或危险)的副作用.但是定义不直接考虑方法/函数递归调用(在维基百科定义中显示"封闭递归",但听起来很奇怪,因为"打开递归"并不是指递归调用).
你知道为什么定义中有"递归"这个词吗?是因为它是基于另一个我不知道的计算机科学定义吗?应该简单地说"动态调度"还不够吗?
根据Apple的说法:
使用
dynamic修饰符标记成员声明时,始终会动态调度对该成员的访问.因为dynamic使用Objective-C运行时调度使用修饰符标记的声明,所以使用该@objc属性隐式标记它们.
根据维基百科:
动态分派是选择在运行时调用多态操作(方法或函数)的实现的过程.
当不同的类由于公共继承而包含相同方法的不同实现时,动态分派通常用于面向对象的语言中.例如,假设您有类
A,B和C,B以及从哪里C继承该方法.现在假设是一个类的变量.在运行时,实际上可能具有类型的值,或者通常在编译时无法知道它是什么.foo()AxAxBC
现在,我正在研究 依赖注入框架:Typhoon,当我在从Objective-C类继承的所有类中打开Swift的示例项目时,TyphoonAssembly注入依赖项的所有方法相关都具有dynamic以下方式包含的修饰符:
public dynamic func weatherReportDao() -> AnyObject {
return TyphoonDefinition.withClass(WeatherReportDaoFileSystemImpl.self)
}
Run Code Online (Sandbox Code Playgroud)
我以为我错过了一些东西,但我不明白在运行时调用多态操作(方法或函数)在哪里.
这里动态调度的目的是什么?
我正在使用已经存在的库,以及我无法访问的源代码.该库代表AST.
我想复制此AST的部分内容,但重命名过程中对变量的引用.由于可以有一个包含Expression对象的AssignCommand-Object,我希望能够使用自己的函数复制每个对象,因此我可以递归地调用它们.但是,由于我无法访问库的代码,我无法添加诸如此类的方法CopyAndRename(string prefix).
因此,我的方法是创建一个Rename具有多个重载的单个函数.因此,我的家庭功能如下:
public static Command Rename(Command cmd, string prefix)
public static AssignCommand Rename(AssignCommand cmd, string prefix)
public static AdditionExpressionRename(AdditionExpression expr, string prefix)
....
Run Code Online (Sandbox Code Playgroud)
函数现在由a组成List<Command>,where AssignCommand是其子类Command.我假设我可以只传递Command给Rename-function,运行时会找到最具体的一个.但是,情况并非如此,所有命令都会传递给Command Rename(Command cmd, string prefix).为什么会这样?有没有办法将调用委托给正确的函数而不使用丑陋的is操作?
我已将此问题解决为以下NUnit-Testcode
using NUnit.Framework;
public class TopClass{
public int retVal;
}
public class SubClassA : TopClass{ }
[TestFixture]
public class ThrowawayTest {
private TopClass Foo (TopClass x) {
x.retVal = 1; …Run Code Online (Sandbox Code Playgroud) 首先,我明白这个问题在现实世界中没有应用,我只是好奇.
想象一下,我们有一个使用单例方法的类:
class Foo
def self.bar
end
end
Run Code Online (Sandbox Code Playgroud)
如果我们调用Foo.bar它,它将首先在每个祖先的单例类中搜索一个方法Foo,然后查看.class方法及其祖先引用的类.我们可以确认,Foo.singleton_class.ancestors返回:
[#<Class:Foo>, #<Class:Object>, #<Class:BasicObject>,
Class, Module, Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)
但是如果我们有一个嵌套的单例类会发生什么,比如:
class Foo
class << self
class << self
def bar
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
如果我们打电话Foo.singleton_class.singleton_class.ancestors,它会返回:
[#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>,
#<Class:#<Class:BasicObject>>, #<Class:Class>, #<Class:Module>,
#<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)
我不明白这个层次结构是如何组织的.
访问者模式是在C++中完成方法参数类型识别(在参数上有效单一调度,而不是成员的类)的最快方法吗?我可能知道我要来调用尚未知亚型的元素的确切方法(S),所以不可避免地使更多的虚拟方法调用就像V::visit(A *)在A::accept(V &v) { v.visit(this); }是不可取的.
// Is the Visitor pattern recommended here? (E inherits D inherits B.)
class Foo {
public:
virtual void visit(B *) { result = 3; }
virtual void visit(D *) { result = 4; }
virtual void visit(E *) { result = 5; }
private:
int result;
}; // class Foo
// Need to add generic interface to B and its children ...
class B {
public:
virtual void accept(class Foo …Run Code Online (Sandbox Code Playgroud) s->duplicate()返回一个类型的对象Box*,但是我在初始化时遇到错误Box*.看起来它正在被转换回来Shape*.如果将协变返回类型转换回基类指针,有什么意义?:
struct Shape
{
virtual Shape* duplicate()
{
return new Shape;
}
};
struct Box : Shape
{
virtual Box* duplicate()
{
return new Box;
}
};
int main()
{
Shape* s = new Box;
Box* b = s->duplicate();
}
Run Code Online (Sandbox Code Playgroud)
错误:
main.cpp:22:12: error: cannot initialize a variable of type 'Box *' with an rvalue of type 'Shape *'
Box* b = s->duplicate();
^ ~~~~~~~~~~~~~~
1 error generated.
Run Code Online (Sandbox Code Playgroud) 假设我要实现一个简单的装饰器@notifyme,在调用装饰函数时打印一条消息。我希望装饰器接受一个参数来打印定制消息;参数(以及参数周围的括号)可以省略,在这种情况下将打印默认消息:
@notifyme('Foo is invoked!')
def foo():
pass
@notifyme # instead of @notifyme()
def bar():
pass
Run Code Online (Sandbox Code Playgroud)
为了允许省略括号,我必须提供以下两种实现@notifyme:
第一个实现允许用户自定义消息,因此它接受一个字符串作为参数并返回一个装饰器:
def notifyme_customized(message: str) -> Callable[[Callable], Callable]:
def decorator(func: Callable) -> Callable:
def decorated_func(*args, **kwargs):
print(str)
return func(*args, **kwargs)
return decorated_func
return decorator
Run Code Online (Sandbox Code Playgroud)
第二个实现本身是一个装饰器,并使用第一个实现来打印默认消息:
def notifyme_default(func: Callable) -> Callable:
return notifyme_customized('The function is invoked.')(func)
Run Code Online (Sandbox Code Playgroud)
为了使上面的两个实现使用相同的名称notifyme,我曾经functools.singledispatch动态地将调用分派notifyme到两个实现之一:
# This is a complete minimal reproducible example
from functools import singledispatch
from typing import Callable …Run Code Online (Sandbox Code Playgroud) dynamic-dispatch ×10
c++ ×2
inheritance ×2
oop ×2
swift ×2
c# ×1
callable ×1
covariance ×1
definition ×1
dynamic ×1
generics ×1
haskell ×1
ios ×1
mono ×1
object-model ×1
overloading ×1
polymorphism ×1
python ×1
python-3.8 ×1
ruby ×1
rust ×1
this ×1
traits ×1