Jensen's Device: Difference between revisions
(added ocaml) |
|||
Line 100: | Line 100: | ||
foo = runST $ do |
foo = runST $ do |
||
i <- newSTRef |
i <- newSTRef 42 -- initial value doesn't matter |
||
sum' i 1 100 $ return recip `ap` readSTRef i |
sum' i 1 100 $ return recip `ap` readSTRef i |
||
Line 106: | Line 106: | ||
</pre> |
</pre> |
||
Output: 5.187377517639621 |
Output: 5.187377517639621 |
||
=={{header|OCaml}}== |
|||
{{trans|Python}} |
|||
<pre> |
|||
let i = ref 42 (* initial value doesn't matter *) |
|||
let sum' ref_i lo hi term = |
|||
let rec aux acc i = |
|||
if i > hi then |
|||
acc |
|||
else begin |
|||
ref_i := i; |
|||
aux (acc +. term ()) (i+1) |
|||
end |
|||
in |
|||
aux 0. lo |
|||
let () = |
|||
Printf.printf "%f\n" (sum' i 1 100 (fun () -> 1. /. float !i)) |
|||
</pre> |
|||
Output: 5.187378 |
|||
=={{header|Python}}== |
=={{header|Python}}== |
Revision as of 04:52, 22 November 2008
This page uses content from Wikipedia. The original article was at Jensen's Device. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance) |
You are encouraged to solve this task according to the task description, using any language you may know.
This task is an exercise in call by name.
Jensen's Device is a computer programming technique devised by Danish computer scientist Jensen after studying the ALGOL 60 Report.
The following program was proposed to illustrate the technique. It computes the 100th harmonic number:
begin integer i; real procedure sum (i, lo, hi, term); value lo, hi; integer i, lo, hi; real term; comment term is passed by-name, and so is i; begin real temp; temp := 0; for i := lo step 1 until hi do temp := temp + term; sum := temp end; comment note the correspondence between the mathematical notation and the call to sum; print (sum (i, 1, 100, 1/i)) end
The above exploits call by name to produce the correct answer (5.187...). It depends on the assumption that an expression passed as an actual parameter to a procedure would be re-evaluated every time the corresponding formal parameter's value was required. If the last parameter to sum had been passed by value, and assuming the initial value of i were 1, the result would have been 100 × 1/1 = 100.
Moreover, the first parameter to sum, representing the "bound" variable of the summation, must also be passed by name, otherwise it would not be possible to compute the values to be added. (On the other hand, the global variable does not have to use the same identifier, in this case i, as the formal parameter.)
Donald Knuth later proposed the Man or Boy Test as a more rigorous exercise.
ALGOL 68
BEGIN INT i; PROC sum = (REF INT ref i, INT lo, hi, PROC REAL term)REAL: COMMENT term is passed by-name, and so is i COMMENT BEGIN REAL temp; temp := 0; FOR i FROM lo BY 1 TO hi DO ref i := i; temp := temp + term OD; # sum := # temp END; COMMENT note the correspondence between the mathematical notation and the call to sum COMMENT print (sum (i, 1, 100, REAL: 1/i)) END
Output: +5.18737751763962e +0
E
In E, the distinct mutable locations behind assignable variables can be reified as Slot objects. The E language allows a variable name (noun) to be bound to a particular slot, and the slot of an already-bound noun to be extracted, using the &
operator.
(The definition of the outer i has been moved down to emphasize that it is unrelated to the i inside of sum.)
pragma.enable("one-method-object") # "def _.get" is experimental shorthand def sum(&i, lo, hi, &term) { # bind i and term to passed slots var temp := 0 i := lo while (i <= hi) { # E has numeric-range iteration but it creates a distinct temp += term # variable which would not be shared with the passed i i += 1 } return temp } { var i := null sum(&i, 1, 100, def _.get() { return 1/i }) }
1/i
is not a noun, so there is no slot associated with it; so we use def _.get() { return 1/i }
to define a slot object which does the computation when it is read as a slot.
The value returned by the above program (expression) is 5.187377517639621.
This emulation of the original call-by-name is of course unidiomatic; a natural version of the same computation would be:
def sum(lo, hi, f) { var temp := 0 for i in lo..hi { temp += f(i) } return temp } sum(1, 100, fn i { 1/i })
Haskell
import Control.Monad import Control.Monad.ST import Data.STRef sum' ref_i lo hi term = return sum `ap` mapM (\i -> writeSTRef ref_i i >> term) [lo..hi] foo = runST $ do i <- newSTRef 42 -- initial value doesn't matter sum' i 1 100 $ return recip `ap` readSTRef i main = print foo
Output: 5.187377517639621
OCaml
let i = ref 42 (* initial value doesn't matter *) let sum' ref_i lo hi term = let rec aux acc i = if i > hi then acc else begin ref_i := i; aux (acc +. term ()) (i+1) end in aux 0. lo let () = Printf.printf "%f\n" (sum' i 1 100 (fun () -> 1. /. float !i))
Output: 5.187378
Python
<python>class Ref(object):
def __init__(self, value=None): self.value=value
i = Ref() def sum(ref_i, lo, hi, term):
# term is passed by-name, and so is i temp = 0; for i in range(lo,hi+1): ref_i.value = i temp += term() return temp
- note the correspondence between the mathematical notation and the call to sum
print (sum (i, 1, 100, lambda: 1.0/i.value))</python> Output: 5.18737751764