2020-11-29 17:43:20 +01:00
|
|
|
#lang scribble/manual
|
2022-03-05 21:37:24 +01:00
|
|
|
@(require scribble/example racket/sandbox
|
2022-03-06 22:53:33 +01:00
|
|
|
(for-label typed/racket/base "../functions.rkt" dds/utils
|
2022-04-18 01:00:34 +02:00
|
|
|
typed/racket/unsafe
|
2022-04-19 23:14:40 +02:00
|
|
|
(only-in racket stream->list stream-first)))
|
2020-11-29 17:43:20 +01:00
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@(define functions-evaluator
|
|
|
|
(parameterize ([sandbox-output 'string]
|
|
|
|
[sandbox-error-output 'string]
|
|
|
|
[sandbox-memory-limit 50])
|
2022-04-25 23:55:32 +02:00
|
|
|
(make-evaluator 'typed/racket #:requires '("functions.rkt"))))
|
2022-04-24 23:44:02 +02:00
|
|
|
|
|
|
|
@(define-syntax-rule (ex . args)
|
|
|
|
(examples #:eval functions-evaluator . args))
|
|
|
|
|
2022-04-29 16:32:22 +02:00
|
|
|
@(define-syntax-rule (deftype . args)
|
|
|
|
(defidform #:kind "type" . args))
|
|
|
|
|
2020-11-29 17:43:20 +01:00
|
|
|
@title[#:tag "functions"]{dds/functions: Formal Functions}
|
|
|
|
|
2020-11-29 21:45:33 +01:00
|
|
|
@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.).
|
|
|
|
|
2022-03-21 00:04:21 +01:00
|
|
|
@section[#:tag "pseudovariadic"]{Pseudovariadic functions}
|
2022-03-20 19:34:48 +01:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-03-20 19:34:48 +01:00
|
|
|
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.
|
|
|
|
|
2022-03-20 20:42:29 +01:00
|
|
|
@deftogether[(@defform[(pseudovariadic-lambda (id ...+) body ...+)]
|
|
|
|
@defform[(pvλ (id ...+) body ...+)])]{
|
|
|
|
|
|
|
|
Define a pseudovariadic anonymous function.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-03-20 20:42:29 +01:00
|
|
|
(: 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].
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-03-20 20:42:29 +01:00
|
|
|
(: g (-> Boolean * Boolean))
|
|
|
|
(pseudovariadic-define (g x y) (and x y))
|
|
|
|
(g #t #f)
|
|
|
|
(eval:error (g #t #f #t))
|
|
|
|
]}
|
2022-03-20 19:34:48 +01:00
|
|
|
@section[#:tag "tabulating"]{Tabulating functions}
|
2020-11-29 21:45:33 +01:00
|
|
|
|
2022-03-06 23:39:51 +01:00
|
|
|
@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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-03-06 23:39:51 +01:00
|
|
|
(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].
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-03-06 23:39:51 +01:00
|
|
|
(tabulate/strict (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
|
|
|
]}
|
|
|
|
|
2022-03-21 00:04:21 +01:00
|
|
|
@defproc[(tabulate/pv [func (-> a * b)]
|
|
|
|
[doms (Listof (Listof a))])
|
|
|
|
(Listof (Listof (U a b)))]{
|
|
|
|
|
|
|
|
Like @racket[tabulate], but @racket[func]
|
|
|
|
@seclink["pseudovariadic"]{pseudovariadic}.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-03-21 00:04:21 +01:00
|
|
|
(tabulate/pv (pvλ (x y) (and x y)) '((#f #t) (#f #t)))
|
|
|
|
]}
|
|
|
|
|
2022-04-22 15:12:41 +02:00
|
|
|
@defproc[(tabulate/list [func (-> (Listof a) b)]
|
|
|
|
[doms (Listof (Listof a))])
|
|
|
|
(Listof (Listof (U a b)))]{
|
|
|
|
|
|
|
|
Like @racket[tabulate/list], but @racket[func] takes its arguments as a list.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-22 15:12:41 +02:00
|
|
|
(tabulate/list (λ ([xs : (Listof Boolean)])
|
|
|
|
(and (car xs) (car xs)))
|
|
|
|
'((#f #t) (#f #t)))
|
|
|
|
]}
|
|
|
|
|
2022-03-06 19:54:05 +01:00
|
|
|
@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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-03-06 19:54:05 +01:00
|
|
|
(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].
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-03-06 19:54:05 +01:00
|
|
|
(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]:
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-03-06 19:54:05 +01:00
|
|
|
(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))))
|
|
|
|
]}
|
|
|
|
|
2022-03-21 00:04:21 +01:00
|
|
|
@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}.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-03-21 00:04:21 +01:00
|
|
|
(tabulate*/pv (list (pvλ (x y) (and x y))
|
|
|
|
(pvλ (x y) (or x y)))
|
|
|
|
'((#f #t) (#f #t)))
|
|
|
|
]}
|
|
|
|
|
2022-04-22 15:12:41 +02:00
|
|
|
@defproc[(tabulate*/list [funcs (Listof (-> (Listof a) b))]
|
|
|
|
[doms (Listof (Listof a))])
|
|
|
|
(Listof (Listof (U a b)))]{
|
|
|
|
|
|
|
|
Like @racket[tabulate*], but the functions in @racket[funcs] take their
|
|
|
|
arguments as a list.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-22 15:12:41 +02:00
|
|
|
(tabulate*/list (list (λ ([xs : (Listof Boolean)])
|
|
|
|
(and (car xs) (cadr xs)))
|
|
|
|
(λ ([xs : (Listof Boolean)])
|
|
|
|
(or (car xs) (cadr xs))))
|
|
|
|
'((#f #t) (#f #t)))
|
|
|
|
]}
|
|
|
|
|
2022-04-21 11:09:52 +02:00
|
|
|
@defproc[(tabulate/pv/boolean [arity Positive-Integer] [func (-> Boolean * Boolean)])
|
2022-04-09 02:03:51 +02:00
|
|
|
(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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-09 02:03:51 +02:00
|
|
|
(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].
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-09 02:03:51 +02:00
|
|
|
(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.
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-04-21 11:09:52 +02:00
|
|
|
@defproc[(tabulate*/pv/boolean [arity Positive-Integer]
|
2022-04-09 02:03:51 +02:00
|
|
|
[func (Listof (-> Boolean * Boolean))])
|
|
|
|
(Listof (Listof Boolean))]{
|
|
|
|
|
|
|
|
Like @racket[tabulate/pv/boolean], but takes a list of functions of the
|
|
|
|
same arity.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-09 02:03:51 +02:00
|
|
|
(tabulate*/pv/boolean 2 (list (pvλ (x y) (and x y))
|
|
|
|
(pvλ (x y) (or x y))))
|
|
|
|
]}
|
|
|
|
|
2022-04-21 11:09:52 +02:00
|
|
|
@defproc[(tabulate/pv/01 [arity Positive-Integer] [func (-> (U Zero One) * (U Zero One))])
|
2022-04-10 22:37:50 +02:00
|
|
|
(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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-10 22:37:50 +02:00
|
|
|
(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.
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-04-21 11:09:52 +02:00
|
|
|
@defproc[(tabulate*/pv/01 [arity Positive-Integer]
|
2022-04-10 22:37:50 +02:00
|
|
|
[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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-10 22:37:50 +02:00
|
|
|
(tabulate*/pv/01 2 `(,(pvλ (x y) (cast (min x y) (U Zero One)))
|
|
|
|
,(pvλ (x y) (cast (max x y) (U Zero One)))))
|
|
|
|
]}
|
|
|
|
|
2022-04-22 16:20:25 +02:00
|
|
|
@defproc[(tabulate/list/boolean [arity Positive-Integer]
|
|
|
|
[func (-> (Listof Boolean) Boolean)])
|
|
|
|
(Listof (Listof Boolean))]{
|
|
|
|
|
|
|
|
Like @racket[tabulate/list], but assumes the domains of all variables of the
|
|
|
|
function are Boolean.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-22 16:20:25 +02:00
|
|
|
(tabulate/list/boolean 2 (λ (xs) (and (car xs) (cadr xs))))
|
|
|
|
]}
|
|
|
|
|
|
|
|
@defproc[(tabulate*/list/boolean [arity Positive-Integer]
|
|
|
|
[funcs (Listof (-> (Listof Boolean) Boolean))])
|
|
|
|
(Listof (Listof Boolean))]{
|
|
|
|
|
|
|
|
Like @racket[tabulate*/list], but assumes the domains of all variables of the
|
|
|
|
function are Boolean.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-22 16:20:25 +02:00
|
|
|
(tabulate*/list/boolean 2 (list (λ (xs) (and (car xs) (cadr xs)))
|
|
|
|
(λ (xs) (or (car xs) (cadr xs)))))
|
|
|
|
]}
|
|
|
|
|
|
|
|
@defproc[(tabulate/list/01 [arity Positive-Integer]
|
|
|
|
[func (-> (Listof (U Zero One)) (U Zero One))])
|
|
|
|
(Listof (Listof (U Zero One)))]{
|
|
|
|
|
|
|
|
Like @racket[tabulate/list], but assumes the domains of all variables of the
|
|
|
|
function are @tt{{0,1}}.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-22 16:20:25 +02:00
|
|
|
(tabulate/list/01 2 (λ (xs)
|
|
|
|
(cast (modulo (+ (car xs) (cadr xs)) 2) (U Zero One))))
|
|
|
|
]}
|
|
|
|
|
|
|
|
@defproc[(tabulate*/list/01 [arity Positive-Integer]
|
|
|
|
[funcs (Listof (-> (Listof (U Zero One)) (U Zero One)))])
|
|
|
|
(Listof (Listof (U Zero One)))]{
|
|
|
|
|
|
|
|
Like @racket[tabulate*/list], but assumes the domains of all variables of the
|
|
|
|
function are @tt{{0,1}}.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-22 16:20:25 +02:00
|
|
|
(tabulate*/list/01
|
|
|
|
2
|
|
|
|
`(,(λ (xs) (cast (min (car xs) (cadr xs)) (U Zero One)))
|
|
|
|
,(λ (xs) (cast (max (car xs) (cadr xs)) (U Zero One)))))
|
|
|
|
]}
|
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@section{Constructing functions}
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-04-13 00:50:53 +02:00
|
|
|
@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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-13 00:50:53 +02:00
|
|
|
(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))
|
|
|
|
]}
|
|
|
|
|
2023-03-03 10:16:20 +01:00
|
|
|
@defproc[(table->unary-function [table (Listof (List a b))])
|
|
|
|
(-> a b)]{
|
|
|
|
|
|
|
|
Like @racket[table->function/list], but the @racket[table] contains
|
|
|
|
exactly 2 columns: one column for the inputs and one column for the
|
|
|
|
outputs, and the result is a unary function.
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
(let ([unary-negation (table->unary-function '((#t #f) (#f #t)))])
|
|
|
|
(unary-negation #t))
|
|
|
|
]}
|
|
|
|
|
2022-04-14 20:46:40 +02:00
|
|
|
@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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-14 20:46:40 +02:00
|
|
|
(define my-and (table->function tab))
|
|
|
|
(my-and #f #t)
|
|
|
|
(my-and #t #t)
|
|
|
|
]}
|
|
|
|
|
2022-04-16 00:17:44 +02:00
|
|
|
@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}.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-16 00:17:44 +02:00
|
|
|
(define my-and/pv (table->function/pv tab))
|
|
|
|
(my-and/pv #f #t)
|
|
|
|
(eval:error (my-and/pv #f))
|
|
|
|
(eval:error (my-and #f))
|
|
|
|
]}
|
|
|
|
|
2022-04-21 11:09:52 +02:00
|
|
|
@defproc[(enumerate-boolean-tables [n Positive-Integer])
|
2022-04-18 01:00:34 +02:00
|
|
|
(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].
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-18 01:00:34 +02:00
|
|
|
(require typed/racket/stream)
|
|
|
|
(stream->list (enumerate-boolean-tables 1))
|
|
|
|
]}
|
|
|
|
|
2022-04-21 11:09:52 +02:00
|
|
|
@defproc[(enumerate-boolean-functions [n Positive-Integer])
|
2022-04-18 23:26:52 +02:00
|
|
|
(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].
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-18 23:26:52 +02:00
|
|
|
(length (stream->list (enumerate-boolean-functions 2)))
|
|
|
|
]}
|
|
|
|
|
2022-04-21 11:09:52 +02:00
|
|
|
@defproc[(enumerate-boolean-functions/pv [n Positive-Integer])
|
2022-04-19 23:14:40 +02:00
|
|
|
(Sequenceof (-> Boolean * Boolean))]{
|
|
|
|
|
|
|
|
Like @racket[enumerate-boolean-functions], but the returned functions are
|
|
|
|
@seclink["pseudovariadic"]{pseudovariadic}.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-19 23:14:40 +02:00
|
|
|
(define bool-f1/pv (stream-first (enumerate-boolean-functions/pv 2)))
|
|
|
|
(bool-f1/pv #f #f)
|
|
|
|
(eval:error (bool-f1/pv #f))
|
|
|
|
]}
|
|
|
|
|
2022-04-19 23:31:28 +02:00
|
|
|
@defproc[(enumerate-boolean-functions/list
|
2022-04-21 11:09:52 +02:00
|
|
|
[n Positive-Integer])
|
2022-04-19 23:31:28 +02:00
|
|
|
(Sequenceof (-> (Listof Boolean) Boolean))]{
|
|
|
|
|
|
|
|
Like @racket[enumerate-boolean-functions], but the returned functions take
|
|
|
|
their arguments as a single list.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-19 23:31:28 +02:00
|
|
|
(define bool-f1/list (stream-first (enumerate-boolean-functions/list 2)))
|
|
|
|
(bool-f1/list '(#f #f))
|
|
|
|
]}
|
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@section{Random functions}
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-04-21 11:09:52 +02:00
|
|
|
@defproc[(random-boolean-table [n Positive-Integer]) (Listof (Listof Boolean))]{
|
2022-04-21 10:46:21 +02:00
|
|
|
|
|
|
|
Generates a random truth table for a Boolean function of arity @racket[n].
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-21 10:46:21 +02:00
|
|
|
(random-boolean-table 2)
|
|
|
|
]}
|
|
|
|
|
2022-04-21 11:09:52 +02:00
|
|
|
@defproc[(random-boolean-function [n Positive-Integer]) (-> Boolean * Boolean)]{
|
2022-04-21 11:03:57 +02:00
|
|
|
|
|
|
|
Generates a random Boolean function of arity @racket[n].
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-21 11:03:57 +02:00
|
|
|
(define random-bool-f (random-boolean-function 2))
|
|
|
|
(random-bool-f #t #f)
|
|
|
|
]}
|
|
|
|
|
2022-04-21 11:23:36 +02:00
|
|
|
@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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-21 11:23:36 +02:00
|
|
|
(define random-bool-f/list (random-boolean-function/list 2))
|
|
|
|
(random-bool-f/list '(#t #f))
|
|
|
|
]}
|
2022-04-21 11:03:57 +02:00
|
|
|
|
2023-03-26 23:29:51 +02:00
|
|
|
@section[#:tag "tbf"]{Threshold Boolean functions}
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-04-21 11:42:50 +02:00
|
|
|
@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.
|
|
|
|
|
2022-04-27 19:00:13 +02:00
|
|
|
Instances of @racket[tbf] have the type @racket[TBF].
|
|
|
|
|
2022-04-21 11:42:50 +02:00
|
|
|
}
|
|
|
|
|
2022-04-29 16:32:22 +02:00
|
|
|
@deftype[TBF]{
|
2022-04-27 19:00:13 +02:00
|
|
|
|
|
|
|
The type of the instances of @racket[tbf]:
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
(tbf #(1 2) 3)
|
|
|
|
]}
|
|
|
|
|
|
|
|
@deftogether[(@defproc[(tbf-w [t TBF]) (Vectorof Real)]
|
|
|
|
@defproc[(tbf-θ [t TBF]) Real])]{
|
2022-04-21 11:50:56 +02:00
|
|
|
|
|
|
|
Shortcuts for @racket[tbf-weights] and @racket[tbf-threshold].
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-04-21 14:06:23 +02:00
|
|
|
@defproc[(boolean->01/vector [bool-v (Vectorof Boolean)])
|
2022-04-21 13:49:15 +02:00
|
|
|
(Vectorof (U Zero One))]{
|
|
|
|
|
|
|
|
Converts a Boolean vector to a vector of zeros and ones.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-21 14:06:23 +02:00
|
|
|
(boolean->01/vector #(#t #f #f))
|
2022-04-21 13:49:15 +02:00
|
|
|
]}
|
|
|
|
|
2022-04-27 19:00:13 +02:00
|
|
|
@defproc[(apply-tbf [t TBF] [inputs (Vectorof (U Zero One))]) (U Zero One)]{
|
2022-04-21 14:20:22 +02:00
|
|
|
|
|
|
|
Applies the TBF to its inputs.
|
|
|
|
|
|
|
|
Applying a TBF consists in multiplying the weights by the corresponding inputs
|
|
|
|
and comparing the sum of the products to the threshold. If the product is
|
|
|
|
above the threshold, the function is 1, otherwise it is 0.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-21 14:20:22 +02:00
|
|
|
(define simple-tbf (tbf #(2 -2) 1))
|
|
|
|
(tabulate/pv/01 2 (pvλ (x y) (apply-tbf simple-tbf (vector x y))))
|
|
|
|
]}
|
|
|
|
|
2022-04-27 19:00:13 +02:00
|
|
|
@defproc[(apply-tbf/boolean [t TBF] [inputs (Vectorof Boolean)]) Boolean]{
|
2022-04-21 15:00:24 +02:00
|
|
|
|
|
|
|
Like @racket[apply-tbf], but takes Boolean values as inputs and outputs
|
|
|
|
a Boolean value.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-21 15:00:24 +02:00
|
|
|
(define simple-tbf (tbf #(2 -2) 1))
|
|
|
|
(tabulate/pv/boolean 2 (pvλ (x y) (apply-tbf/boolean simple-tbf (vector x y))))
|
|
|
|
]}
|
|
|
|
|
2022-04-27 19:00:13 +02:00
|
|
|
@defproc[(list->tbf [lst (Listof Real)]) TBF]{
|
2022-04-21 15:00:50 +02:00
|
|
|
|
|
|
|
Converts a list of numbers to a TBF. The last element of the list is taken to
|
|
|
|
be the threshold, while the other elements are taken to be the weights.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-21 15:00:50 +02:00
|
|
|
(list->tbf '(1 2 3))
|
|
|
|
]}
|
|
|
|
|
2022-04-27 19:00:13 +02:00
|
|
|
@defproc[(lists->tbfs [lsts (Listof (Listof Real))]) (Listof TBF)]{
|
2022-04-21 15:37:55 +02:00
|
|
|
|
|
|
|
Converts multiple lists of numbers to a list of TBFs.
|
|
|
|
|
|
|
|
The main use is for reading TBFs from Org-mode tables read by
|
|
|
|
@racket[read-org-sexp].
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-21 15:37:55 +02:00
|
|
|
(lists->tbfs '((1 2 3) (2 3 4)))
|
|
|
|
]}
|
|
|
|
|
2022-04-21 16:56:58 +02:00
|
|
|
@defproc[(read-org-tbfs [str String] [#:headers headers Boolean #f])
|
2022-04-27 19:00:13 +02:00
|
|
|
(Listof TBF)]{
|
2022-04-21 16:56:58 +02:00
|
|
|
|
|
|
|
Reads a list of TBF from an Org-mode string containing a sexp, containing
|
|
|
|
a list of lists of numbers. If headers is @racket[#t], drops the first list,
|
|
|
|
supposing that it contains the headers of the table.
|
|
|
|
|
|
|
|
The input is typically what @racket[read-org-sexp] reads.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-21 16:56:58 +02:00
|
|
|
(read-org-tbfs "((1 2 1) (1 0 1))")
|
2022-04-25 23:46:22 +02:00
|
|
|
(read-org-tbfs "((x y f) (1 2 1) (1 0 1))" #:headers #t)
|
2022-04-21 16:56:58 +02:00
|
|
|
]}
|
|
|
|
|
2022-04-27 19:00:13 +02:00
|
|
|
@defproc[(tbf-tabulate* [tbfs (Listof TBF)])
|
2022-04-24 23:25:10 +02:00
|
|
|
(Listof (Listof (U Zero One)))]{
|
|
|
|
|
|
|
|
Tabulates a list of TBFs.
|
|
|
|
|
|
|
|
The result is a list of lists describing the truth table of the given TBFs.
|
|
|
|
The first elements of each line give the values of the inputs, while the last
|
|
|
|
elements give the values of each the functions corresponding to the input.
|
|
|
|
|
|
|
|
All the TBFs in @racket[tbfs] must have the same number of inputs as the first
|
|
|
|
TBF in the list.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex[
|
2022-04-24 23:25:10 +02:00
|
|
|
(tbf-tabulate* (list (tbf #(2 2) 1) (tbf #(1 1) 1)))
|
|
|
|
]}
|
2022-04-21 16:56:58 +02:00
|
|
|
|
2022-04-27 19:00:13 +02:00
|
|
|
@defproc[(tbf-tabulate [t TBF])
|
2022-04-25 00:07:28 +02:00
|
|
|
(Listof (Listof (U Zero One)))]{
|
|
|
|
|
|
|
|
Tabulates a single TBF.
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
(tbf-tabulate (tbf #(1 2) 1))
|
|
|
|
]}
|
|
|
|
|
2022-04-27 19:00:13 +02:00
|
|
|
@defproc[(tbf-tabulate*/boolean [tbfs (Listof TBF)])
|
2022-04-25 00:17:03 +02:00
|
|
|
(Listof (Listof Boolean))]{
|
|
|
|
|
|
|
|
Tabulates a list of TBFs like @racket[tbf-tabulate*], but uses Boolean values
|
|
|
|
@racket[#f] and @racket[#t] instead of 0 and 1.
|
|
|
|
|
|
|
|
All the TBFs in @racket[tbfs] must have the same number of inputs as the first
|
|
|
|
TBF in the list.
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
(tbf-tabulate*/boolean (list (tbf #(1 2) 1)))
|
|
|
|
]}
|
|
|
|
|
2022-04-27 19:00:13 +02:00
|
|
|
@defproc[(sbf? [t TBF]) Boolean]{
|
2022-04-25 00:24:32 +02:00
|
|
|
|
|
|
|
A sign Boolean function (SBF) is a TBF whose threshold is 0.
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
(sbf? (tbf #(1 2) 3))
|
|
|
|
(sbf? (tbf #(1 2) 0))
|
|
|
|
]}
|
|
|
|
|
2022-04-27 19:00:13 +02:00
|
|
|
@defproc[(sbf [w (Vectorof Real)]) TBF]{
|
2022-04-25 00:31:00 +02:00
|
|
|
|
|
|
|
Creates a TBF which is an SBF from a vector of weights.
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
(sbf #(1 -1))
|
|
|
|
]}
|
|
|
|
|
2022-04-25 23:29:48 +02:00
|
|
|
@defproc[(list->sbf [lst (Listof Real)])
|
2022-04-27 19:00:13 +02:00
|
|
|
TBF]{
|
2022-04-25 23:29:48 +02:00
|
|
|
|
|
|
|
Converts a list of numbers to an SBF. The elements of the list are taken to be
|
|
|
|
the weights of the SBF.
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
(list->sbf '(1 -1))
|
|
|
|
]}
|
|
|
|
|
2022-04-25 23:43:25 +02:00
|
|
|
@defproc[(read-org-sbfs [str String] [#:headers headers Boolean #f])
|
2022-04-27 19:00:13 +02:00
|
|
|
(Listof TBF)]{
|
2022-04-25 23:43:25 +02:00
|
|
|
|
|
|
|
Reads a list of SBF from an Org-mode string containing a sexp, containing
|
|
|
|
a list of lists of numbers. If headers is @racket[#t], drops the first list,
|
|
|
|
supposing that it contains the headers of the table.
|
|
|
|
|
|
|
|
The input is typically what @racket[read-org-sexp] reads.
|
|
|
|
|
|
|
|
@ex[
|
|
|
|
(read-org-sbfs "((1 1) (1 -1))")
|
|
|
|
]
|
|
|
|
|
|
|
|
See also @racket[read-org-tbfs].
|
|
|
|
}
|
2022-04-09 01:12:17 +02:00
|
|
|
@section[#:tag "fuctions/untyped"]{Untyped definitions}
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-04-25 23:55:32 +02:00
|
|
|
@defmodule[(submod dds/functions untyped)]
|
2022-03-06 22:53:33 +01:00
|
|
|
|
2022-04-10 19:55:28 +02:00
|
|
|
@(require (for-label (only-in racket/contract/base listof any/c)
|
2022-04-25 23:55:32 +02:00
|
|
|
(for-label (only-in (submod "../functions.rkt" untyped)
|
2022-04-10 19:55:28 +02:00
|
|
|
tabulate/boolean tabulate*/boolean
|
|
|
|
tabulate/01 tabulate*/01))))
|
2022-04-09 01:12:17 +02:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2022-04-10 19:55:28 +02:00
|
|
|
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".
|
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@(define functions-evaluator/untyped
|
|
|
|
(parameterize ([sandbox-output 'string]
|
|
|
|
[sandbox-error-output 'string]
|
|
|
|
[sandbox-memory-limit 50])
|
2022-04-25 23:55:32 +02:00
|
|
|
(make-evaluator 'racket #:requires '((submod "functions.rkt" untyped)))))
|
2022-04-09 01:12:17 +02:00
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@(define-syntax-rule (ex/untyped . args)
|
|
|
|
(examples #:eval functions-evaluator/untyped . args))
|
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex/untyped[
|
2022-04-09 01:12:17 +02:00
|
|
|
(tabulate (λ (x y) (and x y)) '((#f #t) (#f #t)))
|
2022-03-06 22:53:33 +01:00
|
|
|
]}
|
|
|
|
|
2022-04-09 01:12:17 +02:00
|
|
|
@defproc[(tabulate* [funcs (listof procedure?)]
|
|
|
|
[doms (listof list?)])
|
|
|
|
(listof list?)]{
|
2020-11-29 21:45:33 +01:00
|
|
|
|
2022-04-10 19:55:28 +02:00
|
|
|
Like @racket[tabulate] (untyped), but @racket[funcs] is a list of functions
|
|
|
|
taking the same arguments over the same domains.
|
2020-11-29 21:45:33 +01:00
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex/untyped[
|
2022-04-09 01:12:17 +02:00
|
|
|
(tabulate* (list (λ (x y) (and x y))
|
|
|
|
(λ (x y) (or x y)))
|
|
|
|
'((#f #t) (#f #t)))
|
|
|
|
]}
|
2022-04-09 01:32:54 +02:00
|
|
|
|
|
|
|
@defproc[(tabulate/boolean [func procedure?]) (listof (listof boolean?))]{
|
|
|
|
|
2022-04-10 19:55:28 +02:00
|
|
|
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.
|
2022-04-09 01:32:54 +02:00
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex/untyped[
|
2022-04-09 01:32:54 +02:00
|
|
|
(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.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex/untyped[
|
2022-04-09 01:32:54 +02:00
|
|
|
(tabulate*/boolean `(,(λ (x y) (and x y))
|
|
|
|
,(λ (x y) (or x y))))
|
|
|
|
]}
|
2022-04-10 19:36:00 +02:00
|
|
|
|
|
|
|
@defproc[(tabulate/01 [func procedure?]) (listof (listof (or/c 0 1)))]{
|
|
|
|
|
2022-04-10 19:55:28 +02:00
|
|
|
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
|
2022-04-10 19:36:00 +02:00
|
|
|
error to supply a function of variable arity.
|
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex/untyped[
|
2022-04-10 19:36:00 +02:00
|
|
|
(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)))]{
|
|
|
|
|
2022-04-10 19:55:28 +02:00
|
|
|
Like @racket[tabulate/01], but takes a list of functions of the same arity.
|
2022-04-10 19:36:00 +02:00
|
|
|
|
2022-04-24 23:44:02 +02:00
|
|
|
@ex/untyped[
|
2022-04-10 19:36:00 +02:00
|
|
|
(tabulate*/01 `(,(λ (x y) (min x y)) ,(λ (x y) (max x y))))
|
|
|
|
]}
|