简体   繁体   English

变量没有符号的语言如何处理动态调度/调用?

[英]How do languages whose variables don't have sigils deal with dynamic dispatch/invocation?

Dynamic languages allow dispatching with and invoking on values from variables whose values are only known at run-time. 动态语言允许从值仅在运行时获知的变量调度和调用值。 Contrasting examples in Perl: Perl中的对比示例:

  1. class names 班级名称

    • constant 不变

       Foo::Bar->some_method Foo::Bar::->some_method 'Foo::Bar'->some_method 

    These are all identical, except the first one is an edge case. 这些都是相同的,除了第一个是边缘情况。 If there's a subroutine defined in scope with that name, the dispatch happens on its return value, which leads to difficult to understand bugs. 如果在具有该名称的范围中定义了子例程,则调度发生在其返回值上,这导致难以理解的错误。 The quoted versions are always safe. 引用的版本总是安全的。

    • dynamic 动态

       my $class_name = 'Foo::Bar'; $class_name->some_method 
  2. method names 方法名称

    • constant 不变

       Some::Class->foo_bar 
    • dynamic 动态

       my $method_name = 'foo_bar'; Some::Class->$method_name 
  3. function names 功能名称

    • constant 不变

       foo_bar; (\\&foo_bar)->() 
    • dynamic 动态

       my $function_name = 'foo_bar'; (\\&$function_name)->() 

I wonder, how do languages whose variable names have no sigils (normally, or at all) deal with these problems, specifically how did their language designers disambiguate the following? 我想知道,变量名称没有符号(通常或根本没有)的语言如何处理这些问题,特别是他们的语言设计者如何消除以下歧义?

  1. resolving class name FooBar.some_method where class FooBar might be name literal or a variable whose value is a class name 解析类名FooBar.some_method ,其中类FooBar可能是name literal或其值为类名的变量
  2. dispatching to SomeClass.foo_bar where method foo_bar might be name literal or a variable whose value is a method name 调度到SomeClass.foo_bar ,其中方法foo_bar可能是name literal或其值为方法名称的变量
  3. invoking foo_bar where the function might be a name literal or a variable whose value is a function 调用foo_bar ,其中函数可能是名称文字或值为函数的变量

I'm primarily interested in the three languages mentioned in this question's tags, but if you know a different dynamic language with no sigils, you can answer too. 我主要对这个问题标签中提到的三种语言感兴趣,但是如果你知道一种不同的动态语言,你也可以回答。

In python and js (my ruby is a bit rusty) it's not possible to use a string in the name context. 在python和js中(我的ruby有点生疏),在名称上下文中不可能使用字符串。 If you attempt to do so, that would be interpreted as an operation on the string itself: 如果您尝试这样做,那将被解释为对字符串本身的操作:

class_name = 'Foo'
object = class_name()

> TypeError: 'str' object is not callable

The string should first be resolved by looking up in the context/scope dictionary: 首先应通过查找上下文/范围字典来解析字符串:

class Foo:
   ....

object = globals()['Foo']()

or 要么

function Foo() ....

object = new window['Foo'];

Python provides both global and local dicts, js only the global one, so there's no way, apart from ubiquitous "eval", to obtain a local value from its name. Python提供全局和本地dicts,js只提供全局dicts,因此除了无处不在的“eval”之外,从名称中获取本地值是没有办法的。

The same applies to methods: you look up a method using getattr (python) or the indirect reference operator [...] (js): 这同样适用于方法:使用getattr (python)或间接引用运算符[...] (js)查找方法:

method_name = 'foo'
method = getattr(some_object, method_name)
method()

Javascript code is slightly more complicated, because, unlike python, the returned method pointer is unbound: Javascript代码稍微复杂一些,因为与python不同,返回的方法指针是未绑定的:

method_name = 'foo'
method = some_object[method_name].bind(some_object)
method()

Without bind you won't get the correct this in a method: 如果没有bind ,你不会得到正确的this一个方法:

 var obj = { xyz: 42, method: function() { document.write(this.xyz) } } var method_name = 'method'; var unbound_method = obj[method_name]; unbound_method() // prints undefined var bound_method = obj[method_name].bind(obj); bound_method() // prints 42 

To put it more formally, the dot operator . 更正式地说,点运算符. (equivalent to php's :: and -> ) in python/js requires an identifier on the right, and allows for arbitrary expressions on the left. (相当于php的::-> )在python / js中需要右侧的标识符,并允许左侧的任意表达式。 Since everything is an object, if the left-hand expression returns a string, the dot applies to that string, not to a variable whose name this string happens to be equal to. 由于一切都是对象,如果左侧表达式返回一个字符串,则该点应用于该字符串,而不是该字符串恰好等于的变量。

(As a side note, it's technically feasible to allow expressions on the right of the dot as well, so that foo.('bar' + 'baz') would resolve to foo.barbaz . I'm not aware of languages that support this syntax, but it looks like a nice alternative to getattr and similar methods). (作为旁注,在点的右侧允许表达式在技术上是可行的,因此foo.('bar' + 'baz')将解析为foo.barbaz 。我不知道支持的语言这个语法,但它看起来像getattr和类似方法的一个很好的替代品)。

Similarly, the call operator () allows for complex expressions on the left, additionally this expression must resolve to a "callable" object. 类似地,调用operator ()允许左侧的复杂表达式,此外该表达式必须解析为“可调用”对象。 Therefore, "someString"() doesn't make sense, since strings are normally not callable (unless you hack them somehow so that they are). 因此, "someString"()没有意义,因为字符串通常不可调用(除非你以某种方式破解它们以便它们)。

That is, if you have a string the contains a variable name, you have to explicitly resolve it before use. 也就是说,如果您有一个包含变量名称的字符串,则必须在使用前明确解析它。 There's no magic in the language that does that for you behind the scenes. 在幕后为你做的那种语言没有神奇之处。

In Ruby, you can always use const_get and send : 在Ruby中,您始终可以使用const_getsend

class FooBar
    def some_method
        return 42
    end
end
class_name = 'FooBar'
puts Module.const_get(class_name).new.some_method

class SomeClass
    def foo_bar
        return 23
    end
end
method_name = 'foo_bar'
puts SomeClass.new.send(method_name)

def foo_bar
    return 123
end
function_name = 'foo_bar'
puts send(function_name)

In js, you need a global to do a name lookup: 在js中,您需要一个全局来进行名称查找:

xyz = 100; // notice: no "var" here – it's a global
var varname = 'xyz';
window[varname]; // 100

... Or you can always use eval , but it's most probably going to bite you: ...或者你总是可以使用eval ,但它很可能会咬你:

var x = eval;
x("var y = 10"); // INDIRECT call to eval
window.y; // 10 (it worked)
eval("var y = 11"); // direct call
window.y; // still 10, direct call in strict mode gets a new context

When trying to get a value from an object, you need to know that JS doesn't respect eta conversion . 当试图从对象获取值时,您需要知道JS不尊重eta转换 Let's set up the context to explain. 让我们设置上下文来解释。

var object = {
  x: 10,
  say: function () {
    console.log(this.x);
  }
}

var method = 'say';
// that version (the "eta abstraction" version):
object.say(); // "this" inside of say is correctly "object"
object[method](); // equivalent

// ... isn't equivalent to this (the "eta reduction" version):
var sayit = object[method];
sayit(); // "this" inside of say is incorrectly "window" (and thus the property x won't exist)

// You can use Function#bind to work around that, but it's only available in ES5-compatible browsers (and currently pretty slow on v8/chrome)
var sayit = object[method].bind(object);
sayit(); // "this" inside of say has been forced to "object", and will print 10 correctly

// You can also, of course, close over it:
var sayit = function () { object[method]() };
sayit(); // prints 10

Python does not allow you to treat objects the same as strings containing the names of variables referring to those objects. Python不允许您将对象与包含引用这些对象的变量名称的字符串相同。 If obj is a variable whose value is an object, you can do FooBar.some_method() . 如果obj是一个值为对象的变量,则可以执行FooBar.some_method() If you have a string "FooBar" , you have to do something else entirely. 如果你有一个字符串"FooBar" ,你必须完全做其他事情。 Exactly what you do depends on where you expect to find that variable "FooBar" (ie, is it a global variable, a local variable, an attribute name, or what). 你究竟做什么取决于你期望找到变量"FooBar" (即,它是全局变量,局部变量,属性名称还是什么)。 You must look up the name in whatever namespace you think it should be in, and then perform your operation on the resulting object. 您必须在您认为应该处于的任何名称空间中查找名称,然后对结果对象执行操作。 For instance, if you want to interpret "FooBar" as a global variable, you can do globals()["FooBar"].some_method() . 例如,如果要将"FooBar"解释为全局变量,可以执行globals()["FooBar"].some_method()

The situation is the same for functions, since functions in Python are just objects like any other. 函数的情况是相同的,因为Python中的函数就像任何其他函数一样。 If you have a string "foo_bar" that you think refers to a function named foo_bar in the global namespace, you can do globals()["foo_bar"]() to try to call it. 如果您认为字符串"foo_bar"是指全局命名空间中名为foo_bar的函数,则可以执行globals()["foo_bar"]()来尝试调用它。

For methods, the situation is basically the same, except that for methods you always know what namespace you're trying to look up the method name in: it's the namespace of the object you're trying to call the method on. 对于方法,情况基本相同,除了对于方法,您总是知道您正在尝试查找方法名称的命名空间:它是您尝试调用方法的对象的命名空间。 For this you use the getattr function. 为此,您使用getattr函数。 If you have a string "method_name" and want to call the method of that name on FooBar , you do getattr(FooBar, "method_name")() . 如果你有一个字符串"method_name"并想在FooBar上调用该名称的方法,你可以使用getattr(FooBar, "method_name")()

The getattr approach can also be used to look up global names in another module's namespace. getattr方法还可用于在另一个模块的命名空间中查找全局名称。 If you think function_name refers to a function in another module's global namespace, you can do getattr(other_module, function_name) . 如果您认为function_name引用另一个模块的全局命名空间中的函数,则可以执行getattr(other_module, function_name)

Here are some examples: 这里有些例子:

def some_function():
    print "I am a function!"

class SomeClass(object):
    def some_method(self):
        print "I am a method!"

function_name = "some_function"
class_name = "SomeClass"
method_name = "some_method"

some_function() # call the function
globals()[function_name]() # call the function
getattr(some_module, function_name)() # if the function was in another module

SomeClass() # make an instance of the class
globals()[class_name]() # make an instance of the class
getattr(some_module, class_name)() # if the class was in another module

instance = SomeClass()
instance.some_method() # call the method
getattr(instance, method_name)() # call the method

In short, there is no ambiguity, because Python does not allow you to use the same syntax to try to do things with objects and with strings referring to objects. 简而言之,没有歧义,因为Python不允许您使用相同的语法来尝试使用对象和引用对象的字符串。 Trying to do something like "obj".method() directly is unambiguous in Python: "obj" is a string, so it can only mean you are trying to call the method on the string itself. 尝试直接做"obj".method()类的东西在Python中是明确的: "obj"是一个字符串,所以它只能意味着你试图在字符串本身上调用该方法。 There is no attempt to implicitly "decode" the string to see if it happens to contain a variable name. 没有尝试隐式“解码”字符串以查看它是否恰好包含变量名称。 Also, there is no conceptual difference between the operations for looking up a class, function, or method, because those are all first-class objects in Python. 此外,查找类,函数或方法的操作之间没有概念上的区别,因为这些操作都是Python中的第一类对象。 The procedure is always the same: first, get the namespace where you want to look up the name; 该过程始终是相同的:首先,获取要查找名称的名称空间; then, look it up. 然后,仔细查看。 Both steps have to be explicit. 这两个步骤都必须明确。

It's also worth noting that using this sort of string-based lookup in globals() is usually considered hackish in Python. 值得注意的是,在globals()中使用这种基于字符串的查找通常被认为是Python中的hackish。 Use of getattr and the like is considered okay only when you're dealing with a highly dynamic data structure (eg, reading user data from some kind of self-documenting file that tells you what its own fields are called) or some kind of metaprogramming (eg, a plugin framework). 只有在处理高度动态的数据结构时(例如,从某种自我文档文件中读取用户数据,告诉您自己的字段被调用)或某种元编程,才能使用getattr等等。 (例如,插件框架)。 Using something like globals()["some_class"]() for such simple cases would be considered very unpythonic. 对于这样的简单情况,使用类似globals()["some_class"]()的东西将被认为是非常不合理的。

In a nutshell: the designers of these other languages (and all the others that I know of, for that purpose) didn't create this ambiguity, that a string can be interpreted as an entity name, in the first place. 简而言之:这些其他语言的设计者(以及我所知道的所有其他语言)都没有产生这种模糊性,首先可以将字符串解释为实体名称。 Such a conversion is always explicit. 这种转换总是明确的。

So, there's nothing to disambiguate from. 所以,没有什么可以消除歧义。

Java and other static languages offer tools for reflection through the use of runtime type information. Java和其他静态语言通过使用运行时类型信息提供反射工具。 You need to reintroduce the type information. 您需要重新引入类型信息。

class SomeClass {
     int foo(int x) {
         return x * 2;
     }

     public static void reflectionExample() 
     throws ReflectiveOperationException {
         String someMethodName = "foo";

         SomeClass obj = new SomeClass();

         // You must select the method by both method name and the signature.
         // This is because Java supports overloading.
         Method method = SomeClass.class.getMethod(
             someMethodName, Integer.TYPE
         );

         // You must cast the object back to the right return type.
         // Java will automatically 'box' and 'unbox' the values.
         int result = (Integer) method.invoke(obj, 3);

         assert result == 6;
     }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何让用户脚本处理尚不存在的功能和元素? - How to have your user script deal with functions and elements that don't exist yet? 如何用变量说:如果此ID与此类匹配,则不要这样做 - How to say with variables: If this id matches this class don't do this 使用jQuery选择所有目标没有后缀的链接 - Choose all links whose targets don't have suffixes using jQuery 选择所有其父级没有特定类的元素(使用'.not()') - Selecting all elements whose parents don't have a certain class (using '.not()') 我该如何进行变量设置,以免在以后的功能中重复自己 - How can I make variables so that I don't have to repeat myself in future functions vuex-动态模块没有唯一数据 - vuex- dynamic modules don't have unique data 有很多变量是有序的。 我需要使用 JavaScript 将它们转换为包含其他内容但不想一一进行 - Have a lot of variables that are in order. I need to convert them using JavaScript to contain something else but don't want to do it one by one 如何使变量的值根据其他变量而变化? - How do I make variables whose values change depending on other variables? 你如何拆分AJAX响应,这样你就不必提出多个请求? - How do you split up an AJAX response so you don't have to make several requests? 如何处理高度不同的布局列? - How do you deal with layout columns that have different height?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM