Skip to content

Evaluation strategy

Evaluation strategies are used by programming languages to determine when to evaluate the argument(s) (参数)of a function call (for function, also read: operation, method, or relation) and what kind of value to pass to the function.

总结:这段话是非常重要的,它的描述是非常清楚的;有的时候,我们调用一个函数的时候,传递给函数的参数不是一个值,而是一个表达式;那这就涉及到了一个问题:什么时候evaluate这个表达式的值呢?显然evaluation strategy就是描述这个问题的。

For example, call by value/call by reference specifies that a function application evaluates the argument before it proceeds to the evaluation of the function's body and that it passes two capabilities to the function, namely, the ability to look up the current value of the argument and to modify it via an assignment statement.[1] The notion of reduction strategy in lambda calculus is similar but distinct.

总结:这段话的含义是:call by value/call by reference指定在evaluate function body之前先evaluate argument,也就是它具有look up参数当前值的能力和通过赋值语句对齐修改的能力。

In practical terms, many modern programming languages have converged(融合) on a call-by-value/call-by-reference[clarification needed] evaluation strategy for function calls (C#, Java). Some languages, especially lower-level languages such as C++, combine several notions of parameter passing. Historically, call by value and call by name date back to ALGOL 60, a language designed in the late 1950s. Call by reference is used by PL/I and some Fortran systems.[2] Purely functional languages like Haskell, as well as non-purely functional languages like R, use call by need.

The evaluation strategy is specified by the programming language definition, and is not a function of any specific implementation.

总结:evaluation strategy由编程语言定义指定,而不是任何特定实现的功能。

evaluation strategy 分类

Strict evaluation

Main article: Eager evaluation

In strict evaluation, the arguments to a function are always evaluated completely before the function is applied.

Under Church encoding, eager evaluation of operators maps to strict evaluation of functions; for this reason, strict evaluation is sometimes called "eager". Most existing programming languages use strict evaluation for functions.

总结:也叫eager evaluation

Call by sharing

Call by sharing (also referred to as call by object or call by object-sharing) is an evaluation strategy first named by Barbara Liskov et al. for the language CLU in 1974.[6]It is used by languages such as Python,[7] Iota,[8] Java (for object references), Ruby, JavaScript, Scheme, OCaml, AppleScript, and many others. However, the term "call by sharing" is not in common use; the terminology is inconsistent across different sources. For example, in the Java community, they say that Java is call by value.[9] Call by sharing implies that values in the language are based on objects rather than primitive types, i.e. that all values are "boxed". Can be said to pass by copy of reference(where primitives are boxed before passing and unboxed at called function).

The semantics of call by sharing differ from call by reference: "In particular it is not call by value because mutations of arguments performed by the called routine will be visible to the caller. And it is not call by reference because access is not given to the variables of the caller, but merely to certain objects(这段关于它和call by reference的对比没有理解清楚)"[10]. So e.g. if a variable was passed, it is not possible to simulate an assignment on that variable in the callee's scope[11]. However, since the function has access to the same object as the caller (no copy is made), mutations to those objects, if the objects are mutable, within the function are visible to the caller, which may appear to differ from call by value semantics. Mutations of a mutable object within the function are visible to the caller because the object is not copied or cloned — it is shared. For example, in Python, lists are mutable, so:

def f(list):
    list.append(1)

m = []
f(m)
print(m)

outputs [1] because the append method modifies the object on which it is called.

Assignments within a function are not noticeable to the caller, because, in these languages, passing the variable only means passing (access to) the actual object referred to by the variable, not access to the original (caller's) variable. Since the rebound variable only exists within the scope of the function, the counterpart in the caller retains its original binding. Compare the Python mutation above with this code that binds the formal argument to a new object:

def f(list):
    list = [1]

m = []
f(m)
print(m)

outputs [], because the statement list = [1] reassigns a new list to the variable rather than to the location it references.

For immutable objects, there is no real difference between call by sharing and call by value, except if object identity is visible in the language. The use of call by sharing with mutable objects is an alternative to input/output parameters:[12] the parameter is not assigned to (the argument is not overwritten and object identity is not changed), but the object (argument) is mutated.

Although this term has widespread usage in the Python community, identical semantics in other languages such as Java and Visual Basic are often described as call by value, where the value is implied to be a reference to the object.[citation needed]

Non-strict evaluation

In non-strict evaluation, arguments to a function are not evaluated unless they are actually used in the evaluation of the function body.

总结:

Under Church encoding, lazy evaluation of operators maps to non-strict evaluation of functions; for this reason, non-strict evaluation is often referred to as "lazy". Boolean expressions in many languages use a form of non-strict evaluation called short-circuit evaluation, where evaluation returns as soon as it can be determined that an unambiguous Boolean will result—for example, in a disjunctive expression where true is encountered, or in a conjunctive expression where false is encountered, and so forth. Conditional expressions also usually use lazy evaluation, where evaluation returns as soon as an unambiguous branch will result.

总结:也叫lazy evaluation