First-class functions: Difference between revisions

From Rosetta Code
Content added Content deleted
(added ruby)
Line 20: Line 20:
{{trans|Python}}
{{trans|Python}}


{{works with|ELLA ALGOL 68|Any (with appropriate job cards) - tested with release 1.8.8d.fc9.i386}}
{{works with|ALGOL 68|Standard - no extensions to language used}}
{{works with|ALGOL 68G|Any - tested with release mk15-0.8b.fc9.i386}}
{{works with|ELLA ALGOL 68|Any (with appropriate job cards) - tested with release 1.8.8d.fc9.i386 using non-standard compose}}
Note: Returning <code>PROC (REAL x)REAL: f1(f2(x))</code> from a function apparently
Note: Returning <code>PROC (REAL x)REAL: f1(f2(x))</code> from a function apparently
violates standard '''ALGOL 68''''s scoping rules. [[ALGOL 68G]] warns about this during
violates standard '''ALGOL 68''''s scoping rules. [[ALGOL 68G]] warns about this during
parsing, and then rejects during runtime.
parsing, and then - if run out of scope - rejects during runtime.
<lang algol>MODE F = PROC (REAL)REAL;
<lang algol>MODE F = PROC (REAL)REAL;
OP ** = (REAL x, power)REAL: exp(ln(x)*power);
OP ** = (REAL x, power)REAL: exp(ln(x)*power);
Line 33: Line 35:
# First class functions allow run-time creation of functions from functions #
# First class functions allow run-time creation of functions from functions #
# return function compose(f,g)(x) == f(g(x)) #
# return function compose(f,g)(x) == f(g(x)) #
PROC compose = (F f1, f2)F: ( (REAL x)REAL: f1(f2(x)) );
PROC non standard compose = (F f1, f2)F: (REAL x)REAL: f1(f2(x)); # eg ELLA ALGOL 68RS #
PROC compose = (F f, g)F: ((F f2, g2, REAL x)REAL: f2(g2(x)))(f, g, );

# Or the classic "o" functional operator #
# Or the classic "o" functional operator #
PRIO O = 5;
OP O = (F f1, f2)F: (REAL x)REAL: f1(f2(x));
OP (F,F)F O = compose;


# first class functions should be able to be members of collection types #
# first class functions should be able to be members of collection types #
[]F funclist = (sin, cos, cube);
[]F func list = (sin, cos, cube);
[]F funclisti = (arc sin, arc cos, cube root);
[]F arc func list = (arc sin, arc cos, cube root);


# Apply functions from lists as easily as integers #
# Apply functions from lists as easily as integers #
FOR index TO UPB funclist DO
FOR index TO UPB func list DO
STRUCT(F f, inverse f) this := (funclist[index], funclisti[index]);
STRUCT(F f, inverse f) this := (func list[index], arc func list[index]);
print(((inverse f OF this O f OF this)(.5), new line))
print(((inverse f OF this O f OF this)(.5), new line))
OD</lang>
OD
</lang>
Output:
Output:
<pre>
<pre>

Revision as of 22:12, 16 March 2009

Task
First-class functions
You are encouraged to solve this task according to the task description, using any language you may know.

According to wp:First-class functions, a language has first class functions if:

  • New functions can be created from others at run time
  • Functions can be stored in other collection types of the language
  • Functions can be used as arguments to other functions
  • Functions can be returned as the value of functions.

The above to be accomplished without invoking a compiler or eval/exec or metaprogramming from the main program.

Write a program to create an ordered collection of a mixture of built-in and user defined functions of a real number, together with another ordered collection of their inverses. Try and use sin, asin, cos, acos, and cube, cube-root as the functions. Create a function compose, that given two functions as arguments returns a new function that applys first the second argument to compose to its argument, then the the first argument to compose to the result, i.e:

 composed_function = compose(f,g)
 where composed_function(x) == compose(f,g)(x) == f(g(x))

Applying the compose of a function and its inverse from the two ordered collections of functions in pairs, show that the result in each case is the original value.

The Python example conforms to the above.

ALGOL 68

Translation of: Python
Works with: ALGOL 68 version Standard - no extensions to language used
Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386
Works with: ELLA ALGOL 68 version Any (with appropriate job cards) - tested with release 1.8.8d.fc9.i386 using non-standard compose

Note: Returning PROC (REAL x)REAL: f1(f2(x)) from a function apparently violates standard ALGOL 68's scoping rules. ALGOL 68G warns about this during parsing, and then - if run out of scope - rejects during runtime. <lang algol>MODE F = PROC (REAL)REAL; OP ** = (REAL x, power)REAL: exp(ln(x)*power);

  1. Add a user defined function and its inverse #

PROC cube = (REAL x)REAL: x * x * x; PROC cube root = (REAL x)REAL: x ** (1/3);

  1. First class functions allow run-time creation of functions from functions #
  2. return function compose(f,g)(x) == f(g(x)) #

PROC non standard compose = (F f1, f2)F: (REAL x)REAL: f1(f2(x)); # eg ELLA ALGOL 68RS # PROC compose = (F f, g)F: ((F f2, g2, REAL x)REAL: f2(g2(x)))(f, g, );

  1. Or the classic "o" functional operator #

PRIO O = 5; OP (F,F)F O = compose;

  1. first class functions should be able to be members of collection types #

[]F func list = (sin, cos, cube); []F arc func list = (arc sin, arc cos, cube root);

  1. Apply functions from lists as easily as integers #

FOR index TO UPB func list DO

 STRUCT(F f, inverse f) this := (func list[index], arc func list[index]);
 print(((inverse f OF this O f OF this)(.5), new line))

OD </lang> Output:

+.500000000000000e +0
+.500000000000000e +0
+.500000000000000e +0

E

First, a brief summary of the relevant semantics: In E, every value, including built-in and user-defined functions, "is an object" — it has methods which respond to messages. Methods are distinguished by the given name (verb) and the number of parameters (arity). By convention and syntactic sugar, a function is an object which has a method whose verb is "run".

The relevant mathematical operations are provided as methods on floats, so the first thing we must do is define them as functions.

<lang e>def sin(x) { return x.sin() } def cos(x) { return x.cos() } def asin(x) { return x.asin() } def acos(x) { return x.acos() } def cube(x) { return x ** 3 } def curt(x) { return x ** (1/3) }

def forward := [sin, cos, cube] def reverse := [asin, acos, curt]</lang>

There are no built-in functions in this list, since the original author couldn't easily think of any which had one parameter and were inverses of each other, but composition would work just the same with them.

Defining composition. fn params { expr } is shorthand for an anonymous function returning a value. <lang e>def compose(f, g) {

   return fn x { f(g(x)) }

}</lang>

<lang e> ? def x := 0.5 \ > for i => f in forward { > def g := reverse[i] > println(`x = $x, f = $f, g = $g, compose($f, $g)($x) = ${compose(f, g)(x)}`) > }

x = 0.5, f = <sin>, g = <asin>, compose(<sin>, <asin>)(0.5) = 0.5 x = 0.5, f = <cos>, g = <acos>, compose(<cos>, <acos>)(0.5) = 0.4999999999999999 x = 0.5, f = <cube>, g = <curt>, compose(<cube>, <curt>)(0.5) = 0.5000000000000001</lang>

Note: def g := reverse[i] is needed here because E as yet has no defined protocol for iterating over collections in parallel. Page for this issue.

Haskell

<lang haskell>Prelude> let cube x = x ^ 3 Prelude> let croot x = x ** (1/3) Prelude> let compose f g = \x -> f (g x) -- this is already implemented in Haskell as the "." operator Prelude> -- we could have written "let compose f g x = f (g x)" but we show this for clarity Prelude> let funclist = [sin, cos, cube] Prelude> let funclisti = [asin, acos, croot] Prelude> zipWith (\f inversef -> (compose inversef f) 0.5) funclist funclisti [0.5,0.4999999999999999,0.5] </lang>

OCaml

<lang ocaml># let cube x = x ** 3.;; val cube : float -> float = <fun>

  1. let croot x = x ** (1. /. 3.);;

val croot : float -> float = <fun>

  1. let compose f g = fun x -> f (g x);; (* we could have written "let compose f g x = f (g x)" but we show this for clarity *)

val compose : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun>

  1. let funclist = [sin; cos; cube];;

val funclist : (float -> float) list = [<fun>; <fun>; <fun>]

  1. let funclisti = [asin; acos; croot];;

val funclisti : (float -> float) list = [<fun>; <fun>; <fun>]

  1. List.map2 (fun f inversef -> (compose inversef f) 0.5) funclist funclisti;;

- : float list = [0.5; 0.499999999999999889; 0.5] </lang>

Python

Python 2.X <lang python>>>> #some built in functions and their inverses >>> from math import sin, cos, acos, asin >>> # Add a user defined function and its inverse >>> cube = lambda x: x * x * x >>> croot = lambda x: x ** (1/3.0) >>> # First class functions allow run-time creation of functions from functions >>> # return function compose(f,g)(x) == f(g(x)) >>> compose = lambda f1, f2: ( lambda x: f1(f2(x)) ) >>> # first class functions should be able to be members of collection types >>> funclist = [sin, cos, cube] >>> funclisti = [asin, acos, croot] >>> # Apply functions from lists as easily as integers >>> [compose(inversef, f)(.5) for f, inversef in zip(funclist, funclisti)] [0.5, 0.49999999999999989, 0.5] >>> </lang>

Ruby

<lang ruby>irb(main):001:0> cube = proc {|x| x ** 3} => #<Proc:0xb7cac4b8@(irb):1> irb(main):002:0> croot = proc {|x| x ** (1/3.0)} => #<Proc:0xb7ca40d8@(irb):2> irb(main):003:0> compose = proc {|f,g| proc {|x| f[g[x]]}} => #<Proc:0xb7c9996c@(irb):3> irb(main):004:0> funclist = [Math.method(:sin).to_proc, Math.method(:cos).to_proc, cube] => [#<Proc:0xb7c84be8@(irb):4>, #<Proc:0xb7c84bac@(irb):4>, #<Proc:0xb7cac4b8@(irb):1>] irb(main):005:0> funclisti = [Math.method(:asin).to_proc, Math.method(:acos).to_proc, croot] => [#<Proc:0xb7c7a88c@(irb):5>, #<Proc:0xb7c7a850@(irb):5>, #<Proc:0xb7ca40d8@(irb):2>] irb(main):006:0> funclist.zip(funclisti).map {|f,inversef| compose[inversef, f][0.5] } => [0.5, 0.5, 0.5] </lang>