487 lines
16 KiB
Racket
487 lines
16 KiB
Racket
#lang scribble/manual
|
|
@(require scribble/example racket/sandbox
|
|
(for-label typed/racket/base "../functions.rkt" dds/utils
|
|
typed/racket/unsafe
|
|
(only-in racket stream->list stream-first)))
|
|
|
|
@title[#:tag "functions"]{dds/functions: Formal Functions}
|
|
|
|
@defmodule[dds/functions]
|
|
|
|
This modules provides some definitions for working with functions: tabulating,
|
|
(re)constructing from tables, generating random functions, etc.
|
|
Some definitions of particular kinds of functions are also provided (threshold
|
|
Boolean functions, etc.).
|
|
|
|
@(define functions-evaluator
|
|
(parameterize ([sandbox-output 'string]
|
|
[sandbox-error-output 'string]
|
|
[sandbox-memory-limit 50])
|
|
(make-evaluator 'typed/racket #:requires '((submod "functions.rkt" typed)))))
|
|
|
|
@section[#:tag "pseudovariadic"]{Pseudovariadic functions}
|
|
|
|
Functions for @seclink["tabulating"]{tabulating functions} take as an argument
|
|
a function to tabulate or a list of functions to tabulate. Writing the type of
|
|
such functions in Typed Racket and generalizing on the number of the arguments
|
|
is hard, and using functions with such types seems even harder.
|
|
The @seclink["tabulating"]{following section} contains some examples,
|
|
illustrating among other things the difficulties of typing
|
|
tabulating functions.
|
|
|
|
The type of @racket[apply] does not help in this situation, because Typed
|
|
Racket treats @racket[apply] in
|
|
@hyperlink["https://racket.discourse.group/t/replicating-the-type-of-apply/770/3"]{a
|
|
special way}. This means that a user-defined function with the same type as
|
|
@racket[apply] and directly calling it will not work in the same way.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
apply
|
|
(define myapply apply)
|
|
myapply
|
|
(apply (λ (x y) (and x y)) '(#t #f))
|
|
(eval:error (myapply (λ (x y) (and x y)) '(#t #f)))
|
|
]
|
|
|
|
One way to work around this issue is to write functions which disguise as
|
|
variadic functions of type @racket[(-> a * b)], but which throw an exception
|
|
when they receive a number of arguments different from a given constant value.
|
|
Such functions are called @italic{pseudovariadic functions} in
|
|
this documentation.
|
|
|
|
@deftogether[(@defform[(pseudovariadic-lambda (id ...+) body ...+)]
|
|
@defform[(pvλ (id ...+) body ...+)])]{
|
|
|
|
Define a pseudovariadic anonymous function.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(: f (-> Boolean * Boolean))
|
|
(define f (pseudovariadic-lambda (x y) (and x y)))
|
|
(f #t #f)
|
|
(eval:error (f #t #f #t))
|
|
]}
|
|
|
|
@deftogether[(@defform[(pseudovariadic-define (name id ...+) body ...+)]
|
|
@defform[(pvdefine (id ...+) body ...+)])]{
|
|
|
|
Define a pseudovariadic function called @racket[name].
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(: g (-> Boolean * Boolean))
|
|
(pseudovariadic-define (g x y) (and x y))
|
|
(g #t #f)
|
|
(eval:error (g #t #f #t))
|
|
]}
|
|
@section[#:tag "tabulating"]{Tabulating functions}
|
|
|
|
@defproc[(tabulate [func (-> a ... b)]
|
|
[doms (List (Listof a) ... a)])
|
|
(Listof (Listof (U Any b)))]{
|
|
|
|
Given a function @racket[func] and a list of domains @racket[doms] for each of
|
|
its arguments, in order, produces a list of lists giving the values of
|
|
arguments and the value of the functions for these inputs.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
|
]}
|
|
|
|
@defproc[(tabulate/strict [func (-> a ... b)]
|
|
[doms (List (Listof a) ... a)])
|
|
(Listof (List (List a ...) (Listof b)))]{
|
|
|
|
Like @racket[tabulate], but the types of the arguments of @racket[func]
|
|
explicitly appear in the return type.
|
|
|
|
As of 2022-03-06, I am not able to write the type of a list first containing
|
|
elements of types @racket[a ...], followed by an element of type @racket[b].
|
|
This is why this function returns a list of lists, each containing first a list
|
|
of inputs, and then the output of @racket[func].
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate/strict (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
|
]}
|
|
|
|
@defproc[(tabulate/pv [func (-> a * b)]
|
|
[doms (Listof (Listof a))])
|
|
(Listof (Listof (U a b)))]{
|
|
|
|
Like @racket[tabulate], but @racket[func]
|
|
@seclink["pseudovariadic"]{pseudovariadic}.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate/pv (pvλ (x y) (and x y)) '((#f #t) (#f #t)))
|
|
]}
|
|
|
|
@defproc[(tabulate* [funcs (Listof (-> a ... b))]
|
|
[doms (List (Listof a) ... a)])
|
|
(Listof (Listof (U Any b)))]{
|
|
|
|
Like @racket[tabulate], but @racket[funcs] is a list of functions taking the
|
|
same arguments over the same domains.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate* (list (λ (x y) (and x y))
|
|
(λ (x y) (or x y)))
|
|
'((#f #t) (#f #t)))
|
|
]}
|
|
|
|
@defproc[(tabulate*/strict [funcs (Listof (-> a ... b))]
|
|
[doms (List (Listof a) ... a)])
|
|
(Listof (List (List a ...) (Listof b)))]{
|
|
|
|
Like @racket[tabulate*], but the types of the arguments of the functions
|
|
explicitly appear in the return type.
|
|
|
|
As of 2022-03-06, I am not able to write the type of a list first containing
|
|
elements of types @racket[a ...], followed by a list of elements of type
|
|
@racket[b]. This is why this function returns a list of lists, each containing
|
|
first a list of inputs, and then the list of outputs of @racket[funcs].
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate*/strict (list (λ (x y) (and x y))
|
|
(λ (x y) (or x y)))
|
|
'((#f #t) (#f #t)))
|
|
]
|
|
|
|
The result of @racket[tabulate*] can be obtained by applying
|
|
@racket[append-lists]:
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(require (only-in "utils.rkt" append-lists))
|
|
(append-lists (tabulate*/strict (list (λ (x y) (and x y))
|
|
(λ (x y) (or x y)))
|
|
'((#f #t) (#f #t))))
|
|
]}
|
|
|
|
@defproc[(tabulate*/pv [funcs (Listof (-> a * b))]
|
|
[doms (Listof (Listof a))])
|
|
(Listof (Listof (U a b)))]{
|
|
|
|
Like @racket[tabulate*], but the functions in @racket[funcs]
|
|
are @seclink["pseudovariadic"]{pseudovariadic}.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate*/pv (list (pvλ (x y) (and x y))
|
|
(pvλ (x y) (or x y)))
|
|
'((#f #t) (#f #t)))
|
|
]}
|
|
|
|
@defproc[(tabulate/pv/boolean [arity Positive-Integer] [func (-> Boolean * Boolean)])
|
|
(Listof (Listof Boolean))]{
|
|
|
|
Like @racket[tabulate/pv], but assumes the domains of all variables of the
|
|
function are Boolean. The arity of @racket[func] must be explicitly supplied.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate/pv/boolean 2 (pvλ (x y) (and x y)))
|
|
]
|
|
|
|
Explicitly supplying the arity is necessary because the actual arity of
|
|
a pseudovariadic function cannot be determined programmatically. Note that
|
|
@racket[tabulate] can be applied directly to a function, but the type of
|
|
@racket[tabulate] requires a cast is required the domains argument
|
|
@racket[doms].
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate (λ (x y) (and x y))
|
|
(cast (make-list 2 '(#f #t))
|
|
(List (Listof Boolean) (Listof Boolean))))
|
|
]
|
|
|
|
This cast is what makes it necessary to resort to pseudovariadic functions and
|
|
explicit @racket[arity] to be able to write a type for
|
|
@racket[tabulate/pv/boolean].
|
|
|
|
See also @secref{fuctions/untyped} for simpler, but untyped version of
|
|
this function.
|
|
|
|
}
|
|
|
|
@defproc[(tabulate*/pv/boolean [arity Positive-Integer]
|
|
[func (Listof (-> Boolean * Boolean))])
|
|
(Listof (Listof Boolean))]{
|
|
|
|
Like @racket[tabulate/pv/boolean], but takes a list of functions of the
|
|
same arity.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate*/pv/boolean 2 (list (pvλ (x y) (and x y))
|
|
(pvλ (x y) (or x y))))
|
|
]}
|
|
|
|
@defproc[(tabulate/pv/01 [arity Positive-Integer] [func (-> (U Zero One) * (U Zero One))])
|
|
(Listof (Listof (U Zero One)))]{
|
|
|
|
Like @racket[tabulate/pv], but assumes the domains of all variables of the
|
|
function are @tt{{0,1}}. The arity of @racket[func] must be
|
|
explicitly supplied.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate/pv/01 2 (pvλ (x y)
|
|
(cast (modulo (+ x y) 2)
|
|
(U Zero One))))
|
|
]
|
|
|
|
See @racket[tabulate/pv/boolean] for an explanation of the explicit
|
|
@racket[arity] argument.
|
|
|
|
}
|
|
|
|
@defproc[(tabulate*/pv/01 [arity Positive-Integer]
|
|
[func (Listof (-> (U Zero One) * (U Zero One)))])
|
|
(Listof (Listof (U Zero One)))]{
|
|
|
|
Like @racket[tabulate/pv/01], but takes a list of functions of the same arity.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(tabulate*/pv/01 2 `(,(pvλ (x y) (cast (min x y) (U Zero One)))
|
|
,(pvλ (x y) (cast (max x y) (U Zero One)))))
|
|
]}
|
|
|
|
@section{Constructing functions}
|
|
|
|
@defproc[(table->function/list [table (Listof (Listof a))])
|
|
(-> (Listof a) a)]{
|
|
|
|
Given a table like the one produced by the functions of the @racket[tabulate]
|
|
family, creates a function which has this behaviour.
|
|
|
|
More precisely, given a line of @racket[table] without its last element, the
|
|
function returned by @racket[table->function/list] produces the corresponding
|
|
last element.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(define tab : (Listof (Listof Boolean))
|
|
'((#f #f #f)
|
|
(#f #t #f)
|
|
(#t #f #f)
|
|
(#t #t #t)))
|
|
(define and/list (table->function/list tab))
|
|
(and/list '(#f #t))
|
|
(and/list '(#t #t))
|
|
]}
|
|
|
|
@defproc[(table->function [table (Listof (Listof a))])
|
|
(-> a * a)]{
|
|
|
|
Like @racket[table->function/list], but the resulting function takes a variable
|
|
number of arguments rather than a list of values.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(define my-and (table->function tab))
|
|
(my-and #f #t)
|
|
(my-and #t #t)
|
|
]}
|
|
|
|
@defproc[(table->function/pv [table (Listof (Listof a))])
|
|
(-> a * a)]{
|
|
|
|
Like @racket[table->function], but the resulting function raises an explicit
|
|
error about invalid arity, instead of the @racket[hash-ref]-related error
|
|
raised by the function returned by @racket[table->function]. In other words,
|
|
the returned by @racket[table->function/pv] is
|
|
@seclink["pseudovariadic"]{pseudovariadic}.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(define my-and/pv (table->function/pv tab))
|
|
(my-and/pv #f #t)
|
|
(eval:error (my-and/pv #f))
|
|
(eval:error (my-and #f))
|
|
]}
|
|
|
|
@defproc[(enumerate-boolean-tables [n Positive-Integer])
|
|
(Sequenceof (Listof (Listof Boolean)))]{
|
|
|
|
Returns the stream of the truth tables of all Boolean functions of
|
|
arity @racket[n].
|
|
|
|
There are @tt{2^(2^n)} Boolean functions of arity @racket[n].
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(require typed/racket/stream)
|
|
(stream->list (enumerate-boolean-tables 1))
|
|
]}
|
|
|
|
@defproc[(enumerate-boolean-functions [n Positive-Integer])
|
|
(Sequenceof (-> Boolean * Boolean))]{
|
|
|
|
Returns the stream of all Boolean functions of a given arity @racket[n].
|
|
|
|
There are @tt{2^(2^n)} Boolean functions of arity @racket[n].
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(length (stream->list (enumerate-boolean-functions 2)))
|
|
]}
|
|
|
|
@defproc[(enumerate-boolean-functions/pv [n Positive-Integer])
|
|
(Sequenceof (-> Boolean * Boolean))]{
|
|
|
|
Like @racket[enumerate-boolean-functions], but the returned functions are
|
|
@seclink["pseudovariadic"]{pseudovariadic}.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(define bool-f1/pv (stream-first (enumerate-boolean-functions/pv 2)))
|
|
(bool-f1/pv #f #f)
|
|
(eval:error (bool-f1/pv #f))
|
|
]}
|
|
|
|
@defproc[(enumerate-boolean-functions/list
|
|
[n Positive-Integer])
|
|
(Sequenceof (-> (Listof Boolean) Boolean))]{
|
|
|
|
Like @racket[enumerate-boolean-functions], but the returned functions take
|
|
their arguments as a single list.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(define bool-f1/list (stream-first (enumerate-boolean-functions/list 2)))
|
|
(bool-f1/list '(#f #f))
|
|
]}
|
|
|
|
@section{Random functions}
|
|
|
|
@defproc[(random-boolean-table [n Positive-Integer]) (Listof (Listof Boolean))]{
|
|
|
|
Generates a random truth table for a Boolean function of arity @racket[n].
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(random-boolean-table 2)
|
|
]}
|
|
|
|
@defproc[(random-boolean-function [n Positive-Integer]) (-> Boolean * Boolean)]{
|
|
|
|
Generates a random Boolean function of arity @racket[n].
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(define random-bool-f (random-boolean-function 2))
|
|
(random-bool-f #t #f)
|
|
]}
|
|
|
|
@defproc[(random-boolean-function/list [n Positive-Integer]) (-> (Listof Boolean) Boolean)]{
|
|
|
|
Like @racket[random-boolean-function], but the constructed function takes
|
|
a list of arguments.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(define random-bool-f/list (random-boolean-function/list 2))
|
|
(random-bool-f/list '(#t #f))
|
|
]}
|
|
|
|
@section{Threshold Boolean functions}
|
|
|
|
@defstruct*[tbf ([weights (Vectorof Real)] [threshold Real])]{
|
|
|
|
A threshold Boolean function (TBF) is a pair @tt{(w, θ)}, where @tt{w} is
|
|
a vector of weights and @tt{θ} is the threshold.
|
|
|
|
}
|
|
|
|
@deftogether[(@defproc[(tbf-w [t tbf]) (Vectorof Real)]
|
|
@defproc[(tbf-θ [t tbf]) Real])]{
|
|
|
|
Shortcuts for @racket[tbf-weights] and @racket[tbf-threshold].
|
|
|
|
}
|
|
|
|
@defproc[(boolean->01/vector [bool-v (Vectorof Boolean)])
|
|
(Vectorof (U Zero One))]{
|
|
|
|
Converts a Boolean vector to a vector of zeros and ones.
|
|
|
|
@examples[#:eval functions-evaluator
|
|
(boolean->01/vector #(#t #f #f))
|
|
]}
|
|
|
|
@section[#:tag "fuctions/untyped"]{Untyped definitions}
|
|
|
|
@defmodule[(submod dds/functions typed untyped)]
|
|
|
|
@(require (for-label (only-in racket/contract/base listof any/c)
|
|
(for-label (only-in (submod "../functions.rkt" typed untyped)
|
|
tabulate/boolean tabulate*/boolean
|
|
tabulate/01 tabulate*/01))))
|
|
|
|
This submodule contains some functions which cannot be typed or some functions
|
|
for which Typed Racket cannot produce contracts, i.e. polymorphic functions of
|
|
variable arity. The definitions in this submodule specifically target untyped
|
|
user code.
|
|
|
|
Since the names of some of the definitions in this submodule are the same in
|
|
the main module, and since they are imported in the same namespace for
|
|
rendering this document, some references to untyped definitions may wrongfully
|
|
point to typed definitions. As a tentative fix, all such references are
|
|
accompanied by the explicit mention "untyped".
|
|
|
|
@(define functions-evaluator/untyped
|
|
(parameterize ([sandbox-output 'string]
|
|
[sandbox-error-output 'string]
|
|
[sandbox-memory-limit 50])
|
|
(make-evaluator 'racket #:requires '((submod "functions.rkt" typed untyped)))))
|
|
|
|
@defproc[(tabulate [funcs procedure?]
|
|
[doms (listof list?)])
|
|
(listof list?)]{
|
|
|
|
Given a function @racket[func] and a list of domains @racket[doms] for each of
|
|
its arguments, in order, produces a list of lists giving the values of
|
|
arguments and the value of the functions for these inputs.
|
|
|
|
@examples[#:eval functions-evaluator/untyped
|
|
(tabulate (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
|
]}
|
|
|
|
@defproc[(tabulate* [funcs (listof procedure?)]
|
|
[doms (listof list?)])
|
|
(listof list?)]{
|
|
|
|
Like @racket[tabulate] (untyped), but @racket[funcs] is a list of functions
|
|
taking the same arguments over the same domains.
|
|
|
|
@examples[#:eval functions-evaluator/untyped
|
|
(tabulate* (list (λ (x y) (and x y))
|
|
(λ (x y) (or x y)))
|
|
'((#f #t) (#f #t)))
|
|
]}
|
|
|
|
@defproc[(tabulate/boolean [func procedure?]) (listof (listof boolean?))]{
|
|
|
|
Like @racket[tabulate] (untyped), but assumes the domains of all variables of
|
|
the function are Boolean. @racket[func] must have a fixed arity. It is an
|
|
error to supply a function of variable arity.
|
|
|
|
@examples[#:eval functions-evaluator/untyped
|
|
(tabulate/boolean (lambda (x y) (and x y)))
|
|
]}
|
|
|
|
@defproc[(tabulate*/boolean [funcs (non-empty-listof procedure?)])
|
|
(listof (listof boolean?))]{
|
|
|
|
Like @racket[tabulate/boolean], but takes a list of functions of the
|
|
same arity.
|
|
|
|
@examples[#:eval functions-evaluator/untyped
|
|
(tabulate*/boolean `(,(λ (x y) (and x y))
|
|
,(λ (x y) (or x y))))
|
|
]}
|
|
|
|
@defproc[(tabulate/01 [func procedure?]) (listof (listof (or/c 0 1)))]{
|
|
|
|
Like @racket[tabulate] (untyped), but assumes the domains of all variables of
|
|
the function are @tt{{0,1}}. @racket[func] must have a fixed arity. It is an
|
|
error to supply a function of variable arity.
|
|
|
|
@examples[#:eval functions-evaluator/untyped
|
|
(tabulate/01 (λ (x y) (modulo (+ x y) 2)))
|
|
]
|
|
|
|
The same remarks apply as for @racket[tabulate/boolean] (untyped).
|
|
|
|
}
|
|
|
|
@defproc[(tabulate*/01 [funcs (listof procedure?)]) (listof (listof (or/c 0 1)))]{
|
|
|
|
Like @racket[tabulate/01], but takes a list of functions of the same arity.
|
|
|
|
@examples[#:eval functions-evaluator/untyped
|
|
(tabulate*/01 `(,(λ (x y) (min x y)) ,(λ (x y) (max x y))))
|
|
]}
|