# -*- mode: org; fill-column: 80; -*-
#+TITLE: Exercises
#+AUTHOR: Zelphir Kaltstahl
#+STARTUP: content
#+STARTUP: indent
#+STARTUP: align
#+STARTUP: inlineimages
#+STARTUP: entitiesplain
#+STARTUP: nologdone
#+STARTUP: nologreschedule
#+STARTUP: nologredeadline
#+STARTUP: nologrefile
#+TODO: TODO WIP | DONE
#+DATE: [2021-11-09 Tue]
#+LANGUAGE: English
#+PRIORITIES: A C C
* About
:PROPERTIES:
:CUSTOM_ID: about
:END:
This org file contains solutions to the exercises of &
#34;Elements of ML Programming" by Jeffrey D. Ullman.
* Solutions
:PROPERTIES:
:CUSTOM_ID: solutions
:END:
** 2.1.1
#+name: 2-1-1-a
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
1 + 2 * 3
#+end_src
#+RESULTS: 2-1-1-a
:results:
val it = 7 : int
:end:
#+name: 2-1-1-b
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
5.0 - 4.2 / 1.4
#+end_src
#+RESULTS: 2-1-1-b
:results:
val it = 2.0 : real
:end:
#+name: 2-1-1-c
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
11 div 2 mod 3
#+end_src
#+RESULTS: 2-1-1-c
:results:
val it = 2 : int
:end:
-> ~div~ either has higher precedence than ~mod~ or is first evaluated, because it is on the left.
#+name: 2-1-1-d
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
"foo" ^ "bar" ^ ""
#+end_src
#+RESULTS: 2-1-1-d
:results:
val it = "foobar" : string
:end:
#+name: 2-1-1-e
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
3 > 4 orelse 5 < 6 andalso not (7<>8)
#+end_src
#+RESULTS: 2-1-1-e
:results:
val it = false : bool
:end:
#+name: 2-1-1-f
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
if 6 < 10 then
6.0
else
10.0
#+end_src
#+RESULTS: 2-1-1-f
:results:
val it = 6.0 : real
:end:
#+name: 2-1-1-g
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
0xAB+123
#+end_src
#+RESULTS: 2-1-1-g
:results:
val it = 294 : int
:end:
#+name: 2-1-1-h
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
0xAB < 123
#+end_src
#+RESULTS: 2-1-1-h
:results:
val it = false : bool
:end:
** 2.1.2
#+name: 2-1-2-a
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
8/4
#+end_src
- uses real division with integers
#+name: 2-1-2-b
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
if 2 < 3 then 4
#+end_src
- there is no ~if ... then ...~ expression in Standard ML
#+name: 2-1-2-c
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
1 < 2 and 5 > 3
#+end_src
- using ~and~ as boolean operator, which it is not
#+name: 2-1-2-d
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
6 + 7 DIV 2
#+end_src
- writing ~div~ in capital letters. SML is case sensitive.
#+name: 2-1-2-e
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
4. + 3.5
#+end_src
- ~4.~ is not a complete input. It is not a real number for SML.
#+name: 2-1-2-f
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
1.0 < 2.0 or 3 > 4
#+end_src
- using ~or~ as boolean operator, which it is not meant to be used for.
#+name: 2-1-2-g
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
#"a" ^ #"b"
#+end_src
- using concatenation for strings on characters
#+name: 2-1-2-h
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
123.
#+end_src
- ~123.~ is not a complete input
#+name: 2-1-2-i
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
1.0 = 2.0
#+end_src
- using ~=~ on reals, which is not allowed in SML
** 2.1.3
#+name: 2-1-3
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
print ("\"\\\\\\\" stands for the double-quote character, \\\n \
\\\which otherwise would be interpreted \\\n \
\\\as the string ender.\"\n")
#+end_src
#+RESULTS: 2-1-3
:results:
"\\\" stands for the double-quote character, \
\which otherwise would be interpreted \
\as the string ender."
val it = () : unit
:end:
** 2.1.4
#+name: 2-1-4-a
#+begin_src sml :results output verbatim replace drawer :tangle exercises.sml
val cond1 = true;
val cond2 = false;
(* andalso implementation *)
if cond1 then
cond2
else
false;
(* orelse implementation *)
if cond1 then
true
else
cond2
#+end_src
** 2.2.1
#+name: 2-2-1
#+begin_src sml :results verbatim output replace drawer
(* a *)
floor 123.45;
(* b *)
floor ~123.45;
(* c *)
ceil 123.45;
(* d *)
ceil ~123.45;
(* e *)
ord #"Y";
(* f *)
chr 120;
(* g *)
real (ord #"N");
(* h *)
chr (trunc 97.0);
(* i *)
str #"Z";
#+end_src
#+RESULTS: 2-2-1
:results:
val it = 123 : int
:end:
** 2.2.2
1. ~ceil~ should be applied to reals, not to integers. If we already have an
integer, what is the point of applying ~ceil~?
2. The ~then~ and ~else~ branches return different types of values. A fix would
be to return ~7~, the integer, instead of ~7.0~ the real.
3. ~chr~ can only be used on ~255~ or lower.
4. ~chr~ can only be used on numbers > 0.
5. ~ord~ can only be used on characteres. We could write the /character/ ~#"3"~
instead.
6. ~chr~ can only be used on integers.
7. ~0~ is not a boolean value. Write an expression resulting in a boolean value
as a condition instead of ~0~.
8. ~ord~ can only be used on characters. ~"a"~ is a string. Write the character
~#"a"~ instead.
** 2.3.1
Choice of:
+ alphanumeric identifier (ordinary non-type values)
+ symbolic identifier
+ type representing identifier
+ not a valid identifier
Solution:
1. ~The7Dwarves~: alphanumeric indentifier
2. ~7Dwarves~: not a valid identifier, because it starts with a digit.
3. ~SevenDwarves,The~: not a valid identifier, because it contains a ~,~, which
cannot be part of any identifier.
4. ~'SnowWhite'~: type representing identifier
5. ~a<=b~: not a valid identifier, because it mixes symbolic identifier
characters with alphanumeric identifier characters.
7. ~hurrah!~: not a valid identifier, because it mixes symbolic identifier
characters with alphanumeric identifier characters.
8. ~
#1~: not a valid identifier, mixing symbolic identifier characters with
alphanumeric identifier characters.
9. ~'123~: not a valid identifier. Apparently one cannot have a digit as a first
character after the single quote.
** 2.3.2
- ~a~, ~b~ and ~c~.
** 2.4.1
1. ~4~
2. ~3~
3. ~[4,5]~
4. ~[#"f",#"o",#"o"]~
5. ~"foo"~
6. ~["c","a","t"]~
7. ~["c","o","b","o","l"]~
8. ~"cat"~
** 2.4.2
1. accessing a position that does not exist
2. the empty list has no element in it, so there cannot be a head
3. using a list acccessor with a non-list argument
4. using ~explode~ with a non-string argument
5. using ~implode~ with non-char-list arguments
6. using ~::~ operator with two lists instead of an element and a list
7. the empty list has no tail
8. trying to use the append operator ~@~ with non-lists
9. using ~concat~ with a non-string-list
** 2.4.3
1. ~real * (string * int list)~
2. ~int list list~ (because ~nil~ is an alias for ~[]~)
3. ~(int * real) list~
4. ~char list * int list list~
** 2.4.4
1. No. Tuples have a non-variable length and as such the ML type system
distinguishes between 2-tuples and 3-tuples. Tuples can contain elements of
multiple types, which means that the type system needs to make sure to
provide suficient information by mentioning the type of each element, in
contrast to lists.
2. Yes. Lists are of variable length and the ML type system does not distinguish
between different lengths of lists. Lists can also contain only elements of
the same type, so having the information "int list" is sufficient for the
type system.
** 2.4.5
1. example:
#+begin_src sml
[[[1]]]
#+end_src
2. example:
#+begin_src sml
[(1, #"1")]
#+end_src
3. example:
#+begin_src sml
(["s"], (1, (2.0, "2.5")), 3)
#+end_src
4. example:
#+begin_src sml
(((1, 2), ([true]), 1.0), (2.0, "a"))
#+end_src
5. example:
#+begin_src sml
((true, 1), #"c")
#+end_src
6. example:
#+begin_src sml
(1.0, [[[[1]]]])
#+end_src
** 2.4.6
convert string of length 1 into its character
#+begin_src sml
hd(explode("a"));
#+end_src
** 3.1.1
1. code:
#+begin_src sml
fun cube (x: real) =
x * x * x;
#+end_src
2. code:
#+begin_src sml
fun min3 (a: int, b: int, c: int) =
let
fun min2 (a: int, b: int) =
if a <= b
then a
else b;
in
min2 (min2 (a, b), c)
end;
#+end_src
#+RESULTS:
: val min3 = fn : int * int * int -> int
3. code:
#+begin_src sml
fun third (lst: 'a list) =
hd(tl(tl(lst)));
#+end_src
#+RESULTS:
: val third = fn : 'a list -> 'a
4. code:
#+begin_src sml
fun reverse_3_tup (tup: 'a * 'b * 'c) =
(#3 tup,
#2 tup, #1 tup);
#+end_src
#+RESULTS:
: val reverse_3_tup = fn : 'a * 'b * 'c -> 'c * 'b * 'a
5. code:
#+begin_src sml
fun third_char (str: string) =
hd (
tl (
tl (
explode str
)
)
);
#+end_src
#+RESULTS:
: val third_char = fn : string -> char
6. code:
#+begin_src sml
fun left_shift_list (lst: 'a list) =
tl lst @ [hd lst];
#+end_src
#+RESULTS:
: val left_shift_list = fn : 'a list -> 'a list
** 3.1.2
*** 1
#+begin_src sml
fun max (a: int, b: int) =
if a >= b then a else b;
fun min (a: int, b: int) =
if a <= b then a else b;
fun min_max_out_of_3 (a: int, b: int, c: int) =
[min(min(a, b), c), max(max(a, b), c)];
min_max_out_of_3 (2, 1, 3);
#+end_src
#+RESULTS:
: val max = fn : int * int -> int
*** 2
**** Quicksort
First consider quicksort implemented in Scheme (taken and improved readability
from [[https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#Scheme]] (2021-07-04)):
#+begin_src scheme
(define split-by
(λ (lst pivot-test partitions-processor)
;; loop over elements, recursively partitioning into
;; elements, which satisfy the pivot-test and elements,
;; which do not satisfy the pivot-test.
(let loop ([low '()] [high '()] [lst lst])
(cond
[(null? lst)
;; TODO: Might be able to simply use the empty list
;; here, instead of applying the
;; partitions-processor.
(partitions-processor low high)]
[(pivot-test (car lst))
;; Collect element satisfying the pivot-test.
(loop low
(cons (car lst) high)
(cdr lst))]
[else
;; Collect element not satisfying the pivot-test.
(loop (cons (car lst) low)
high
(cdr lst))]))))
(define quicksort
(λ (lst greater-than?)
(cond
[(null? lst) '()]
[else
(split-by
;; Use only the rest of the list, as the first
;; element is used as pivot element.
(cdr lst)
;; Predicate for determining whether an element is
;; greater than the pivot element. Here naive
;; approach of using the first element as a pivot
;; element.
(λ (x) (greater-than? x (car lst)))
;; Lambda processing partitions. What to do with the
;; 2 partitions after partitioning using the
;; greater-than? predicate as pivot-test.
(λ (low high)
;; Careful: append has O(n) runtime.
(append
;; recursive call for lesser elements
(quicksort low greater-than?)
;; pivot element
(list (car lst))
;; recursive call for greater elements
(quicksort high greater-than?))))])))
(define (quicksort-2 lst gt?)
(if (null? lst)
'()
(append (quicksort (filter (lambda (x) (gt? (car lst) x)) (cdr lst))
gt?)
;; needs to be a list for append to work
(list (car lst))
(quicksort (filter (lambda (x) (not (gt? (car lst) x))) (cdr lst))
gt?))))
#+end_src
We can rewrite this in SMLNJ:
#+name: quicksort
#+begin_src sml
fun quicksort (nil,
greater_than: 'a * 'a -> bool,
partition_pivot_not_pivot: 'a list -> 'a * 'a list) = nil
| quicksort (lst: 'a list,
greater_than: 'a * 'a -> bool,
partition_pivot_not_pivot: 'a list -> 'a * 'a list) =
let
val (pivot_elem, not_pivot_elems) = partition_pivot_not_pivot lst;
in
let
val (low, high) = List.partition (fn x => greater_than(x, pivot_elem)) not_pivot_elems;
in
quicksort(low, greater_than, partition_pivot_not_pivot)
@
[pivot_elem]
@
quicksort(high, greater_than, partition_pivot_not_pivot)
end
end;
#+end_src
**** Solution
And then we can use it as follows:
#+begin_src sml
<>
fun sorted_triple (a: int, b: int, c: int) =
quicksort ([a,b,c],
(fn (a, b) => a > b),
(fn lst => (hd lst, tl lst)));
#+end_src
*** 3
#+begin_src sml
fun round_tenth (num: real) =
Real.fromInt(round(num * 10.0)) / 10.0;
#+end_src
*** 4
#+begin_src sml
fun delete_second (lst: 'a list) =
if length(lst) >= 2
then
let
val (a::b::rest) = lst;
in
a :: rest
end
else
lst;
#+end_src
** 3.1.3
*** 1
8
*** 2
11
*** 3
8
*** 4
10
*** 5
18
*** 6
17
** 3.2.1
*** 1
#+begin_src sml
fun fac 0 = 1
| fac 1 = 1
| fac n =
let
fun iter 1 prod = prod
| iter remaining_factors prod =
iter (remaining_factors - 1) (prod * remaining_factors);
in
if n > 0
then iter n 1
else raise Fail "n must be 0 or greater"
end;
#+end_src
*** 2
#+begin_src sml :results output verbatim drawer replace
fun cycle (lst: 'a list, i: int) =
let
fun left_shift_list (lst: 'a list) =
tl lst @ [hd lst];
in
if i > 0
then
cycle (left_shift_list(lst), (i - 1))
else
lst
end;
cycle ([1,2,3,4,5], 3);
#+end_src
#+RESULTS:
:results:
val cycle = fn : 'a list * int -> 'a list
:end:
*** 3
#+begin_src sml :results output verbatim drawer replace
fun duplicate_elements_of_list (lst: 'a list) =
if null(lst)
then []
else
let
val first = hd lst;
in
first :: first :: duplicate_elements_of_list (tl lst)
end;
duplicate_elements_of_list [1,2,3,4];
#+end_src
#+RESULTS:
:results:
val duplicate_elements_of_list = fn : 'a list -> 'a list
:end:
*** 4
#+begin_src sml :results output verbatim drawer replace
fun len (lst: 'a list) =
if null lst
then
0
else
1 + len(tl(lst));
#+end_src
*** 5
#+begin_src sml :results output verbatim drawer replace
fun pow (base: real, exponent: int) =
let
fun iter (base: real, exponent: int, product: real) =
if exponent = 1
then product * base
else iter (base, (exponent - 1), (product * base));
in
if exponent = 0
then 1.0
else iter (base, exponent, 1.0)
end;
pow (8.5, 3);
#+end_src
*** 6
#+begin_src sml :results output verbatim drawer replace
fun find_largest (lst: real list) =
foldl (fn (elem, prev) => if elem > prev
then elem
else prev)
Real.negInf
lst;
find_largest [
2.0,
7456.0,
3.0,
878.0,
2346.0,
8768.0,
2.0,
2.0,
42.0,
767.0,
8768.0,
35.0,
32.0,
423.0,
92346.0
];
#+end_src
#+RESULTS:
:results:
val find_largest = fn : real list -> real
:end:
** 3.2.2
*** 1
#+begin_src sml :results output verbatim drawer replace
fun foo (a,b,c,d) =
if a = b
then c + 1
else
if a > b
then c
else
b + d;
#+end_src
1. The return types of all branches must agree. So if one branch returns an integer, all branches must return an integer.
2. For overloaded operators like ~+~, the operands need to have the same type. If one operand is an integer, then so must the other be.
3. The branch ~then c + 1~ returns an integer, because ~1~ is an integer and thus ~c~ must be an integer and the operator ~+~ for integers returns and ~int~.
4. Subsequently all other branches must return ~int~.
5. The branch ~else b + d~ must return an ~int~ as well. ~b + d~ will only return an ~int~, if ~b~ and ~d~ are both ~int~.
6. The ~=~ operator can only compare numbers of same type and not of type ~real~.
7. Since we already know the type of ~b~ to be ~int~, ~a~ must also be an ~int~.
** 3.2.3
#+begin_src sml :results output verbatim drawer replace
fun f (a: int, b, c, d, e) = ...
#+end_src
*** 1
#+begin_src sml :results output verbatim drawer replace
if a < b + c then d else e
#+end_src
- a: int
- b: int
- c: int
- d and e of same type
- f returns value of same type as d and e
*** 2
#+begin_src sml :results output verbatim drawer replace
if a < b then c else d
#+end_src
- a: int
- b: int
- c and d have same type
- e is unused and of unknown type
- f returns value of same type as c and d
*** 3
#+begin_src sml :results output verbatim drawer replace
if a < b then b + c else d + e
#+end_src
- a: int
- b: int
- c: int
- d: int
- e: int
- f returns int
*** 4
#+begin_src sml :results output verbatim drawer replace
if a < b then b < c else d
#+end_src
- a: int
- b: int
- c: int
- d: bool
- e is unused and of unknown type
- f returns bool
*** 5
#+begin_src sml :results output verbatim drawer replace
if b < c then a else c + d
#+end_src
- a: int
- b: int
- c: int
- d: int
- e is unused and of unknown type
- f returns int
*** 6
#+begin_src sml :results output verbatim drawer replace
if b < c then d else e
#+end_src
- a: int
- b: int
- c: int
- d and e of same type
- f returns value of same type as d and e have
*** 7
#+begin_src sml :results output verbatim drawer replace
if b < c then d + e else d * e
#+end_src
- a: int
- b and c of same type (implementation assumes ~int~, as long as ~real~ is not specified explicitly as type of the arguments of the function.
- d and e of same type
- f returns value of same type as d and e have
** 3.2.4
*** 1
#+begin_src sml :results output verbatim drawer replace
fun fac 0 = 1
| fac 1 = 1
| fac n =
let
fun iter 1 prod = prod
| iter remaining_factors prod =
iter (remaining_factors - 1) (prod * remaining_factors);
in
if n > 0
then iter n 1
else raise Fail "n must be 0 or greater"
end;
#+end_src
At each iteration ~remaining_factors~ is reduced by ~1~ and ~prod~ multiplied by ~remaining_factors~. The environment does not really change anything else than that, because the stack frame is reused due to tail call opimization. When all is done, ~prod~ is returned and the stack frame cleaned up.
*** reverse
**** reverse using pattern matching (recommended)
#+begin_src sml
fun reverse nil = nil
| reverse (x::xs) =
reverse (xs) @ [x];
#+end_src
**** reverse using null predicate
#+begin_src sml
fun reverse2 (lst: 'a list) =
if null(lst)
then nil
else
reverse2 (tl(lst)) @ [hd(lst)];
#+end_src
**** alternative
#+begin_src sml
fun reverse3 nil = nil
| reverse3 (lst: 'a list) =
let
fun reverse_helper (lst, acc) =
if null(lst)
then acc
else reverse_helper (tl (lst), hd (lst) :: acc);
in
reverse_helper(lst, nil)
end;
#+end_src
**** reverse using foldl
This is actually doing the same as ~reverse3~ above, but perhaps more elegant.
#+begin_src sml
fun reverse4 nil = nil
| reverse4 (lst: 'a list) =
(* cannot call foldl with tuple notation, have to call it with curried notation *)
foldl op:: nil lst;
#+end_src
*** make a long list
#+begin_src sml
fun range (i: int) =
if i = 0
then nil
else i :: range (i - 1);
#+end_src
*** merge two sorted lists into a sorted list
#+begin_src sml
fun merge (nil, nil) = nil
| merge (lst1, nil) = lst1
| merge (nil, lst2) = lst2
| merge (lst1 as x::xs, lst2 as y::ys) =
if y < x
then y :: merge(lst1, ys)
else x :: merge(xs, lst2);
#+end_src
** 3.3.1
*** a
This solution already uses 3 patterns, which are simple numbers or arbitrary things.
#+begin_src sml
fun fac 0 = 1
| fac 1 = 1
| fac n =
let
fun iter 1 prod = prod
| iter remaining_factors prod =
iter (remaining_factors - 1) (prod * remaining_factors);
in
if n > 0
then iter n 1
else raise Fail "n must be 0 or greater"
end;
#+end_src
*** b
#+name: printer
#+begin_src sml :noweb yes :results output verbatim drawer replace
datatype primitives_wrapper =
Int of int | Real of real | String of string
datatype compound_wrapper =
IntList of int list |
RealList of real list |
StringList of string list;
fun int_list_to_string lst =
String.concatWith ", " (map (fn num => Int.toString(num)) lst);
fun real_list_to_string lst =
String.concatWith ", " (map (fn num => Real.toString(num)) lst);
fun string_list_to_string lst =
String.concatWith ", " ((map (fn str => "\"" ^ str ^ "\"") lst));
fun myprint (lst: compound_wrapper) =
case lst of
IntList lst => print("[" ^ (int_list_to_string lst) ^ "]" ^ "\n")
| RealList lst => print("[" ^ (real_list_to_string lst) ^ "]" ^ "\n")
| StringList lst => print("[" ^ (string_list_to_string lst) ^ "]" ^ "\n");
#+end_src
#+begin_src sml :noweb yes :results output verbatim drawer replace
fun left_shift_list nil = nil
| left_shift_list (x::xs) =
xs @ [x];
#+end_src
#+RESULTS:
:results:
val left_shift_list = fn : 'a list -> 'a list
:end:
*** c
#+begin_src sml :results output verbatim drawer replace
fun left_shift_list nil = nil
| left_shift_list (x::xs) =
xs @ [x];
fun cycle (nil, i: int) = nil
| cycle (x::nil, i: int) = [x]
| cycle (xs, 0) = xs
| cycle (xs, i: int) =
if i > 0
then
cycle (left_shift_list(xs), (i - 1))
else
xs;
#+end_src
#+RESULTS:
:results:
val cycle = fn : 'a list * int -> 'a list
:end:
*** d
#+begin_src sml :results output verbatim drawer replace
fun duplicate_elements nil = nil
| duplicate_elements (x::xs) =
x :: x :: duplicate_elements(xs);
#+end_src
#+RESULTS:
:results:
val cycle = fn : 'a list * int -> 'a list
:end:
*** e
The optimal solution would be something like the following:
#+begin_src sml :results output verbatim drawer replace
fun expt (0.0, i: int) = 0.0
| expt (1.0, i: int) = 1.0
| expt (x: real, 0) = 1.0
| expt (x: real, 1) = x
| expt (x: real, i: int) =
let
fun iter (base: real, exponent: int, product: real) =
if exponent = 1
then
product * base
else
iter (base, (exponent - 1), (product * base));
in
iter (x, i, 1.0)
end;
#+end_src
However, pattern matching on reals like that does not work, because reals are not an equal type, a type, which can be compared using equal. So one has to take the x as is and leave those cases away:
#+begin_src sml :results output verbatim drawer replace
fun expt (x, 0) = 1.0
| expt (x, 1) = x
| expt (x, i) =
let
fun iter (base: real, exponent: int, product: real) =
if exponent = 1
then
product * base
else
iter (base, (exponent - 1), (product * base));
in
iter (x, i, 1.0)
end;
#+end_src
#+RESULTS:
:results:
val expt = fn : real * int -> real
:end:
*** f
#+begin_src sml :results output verbatim drawer replace
fun find_max xs =
let
fun iter (nil, x) = x
| iter (x::xs, prev_max) =
if x > prev_max
then
iter (xs, x)
else
iter (xs, prev_max);
in
iter (xs, Real.negInf)
end;
#+end_src
** 3.3.2
#+begin_src sml :results output verbatim drawer replace
fun flip_pairs (x1::x2::xs) = x2 :: x1 :: flip_pairs(xs)
| flip_pairs (x::nil) = x :: nil
| flip_pairs nil = nil;
#+end_src
** 3.3.3
#+begin_src sml :results output verbatim drawer replace
fun remove_nth_member (nil, n) = nil
| remove_nth_member (x::xs, 0) = xs
| remove_nth_member (x::xs, n) =
if n = 0
then
xs
else
x :: remove_nth_member (xs, (n - 1));
#+end_src
#+RESULTS:
:results:
val remove_nth_member = fn : 'a list * int -> 'a list
:end:
** 3.3.4
#+begin_src sml :results output verbatim drawer replace
(1) fun sumLists(nil) = 0
(2) | sumLists(nil::YS) = sumLists(YS)
(3) | sumLists((x::xs)::YS) = x + sumLists(xs::YS);
sumLists([[1, 2], nil, [3]])
-> case 3: x = 1, xs = [2], YS = [nil, [3]]
-> 1 + sumLists([[2], nil, [3]])
-> case 3: x = 2, xs = nil, YS = [nil, [3]]
-> 1 + 2 + sumLists([nil, nil, [3]])
-> case 2: YS = [nil, [3]]
-> 1 + 2 + sumLists([nil, [3]])
-> case 2: YS = [[3]]
-> 1 + 2 + sumLists([[3]])
-> case 3: x = 3, xs = nil, YS = nil
-> 1 + 2 + 3 + sumLists(nil)
-> case 1:
-> 1 + 2 + 3 + 0
-> 6
#+end_src
** 3.3.5
*** a
#+begin_src sml :results output verbatim drawer replace
(* pattern was given as: (x::y::zs, w) *)
(["a", "b", "c"], ["d", "e"])
(*
->
x = "a",
y = "b",
zs = ["c"],
w = ["d", "e"]
,*)
#+end_src
*** b
#+begin_src sml :results output verbatim drawer replace
(* pattern was given as: (x::y::zs, w) *)
(["a", "b"], 4.5)
(*
->
x = "a",
y = "b",
zs = nil,
w = 4.5
,*)
#+end_src
*** c
#+begin_src sml :results output verbatim drawer replace
(* pattern was given as: (x::y::zs, w) *)
([5], [6, 7])
(* -> does not match *)
#+end_src
* 3.3.6
#+begin_src sml
[(x,y),zs]
[,] __ zs
|
|__ (,) __ y
|
|__ x
[,] __ nil
|
|__ (,) __ 3
|
|__ (1,2)
#+end_src
* 3.3.7
#+begin_src sml
open IntInf;
fun square 0 = 0
| square n =
(square (n - 1)) + (2 * n) - 1;
#+end_src
* 3.3.8
#+begin_src sml
fun sort_pairs nil = nil
| sort_pairs ((pair as (elem1, elem2)) :: nil) =
if elem1 > elem2
then (elem2, elem1) :: nil
else pair :: nil
| sort_pairs ((pair as (elem1, elem2)) :: pairs) =
if elem1 > elem2
then (elem2, elem1) :: sort_pairs pairs
else pair :: sort_pairs pairs;
#+end_src
* 3.3.9
#+name: vowel_functions
#+begin_src sml :noweb yes
fun is_vowel c =
let
val vowels = [
#"a", #"e", #"i", #"o", #"u", #"A", #"E", #"I", #"O", #"U"
];
in
case List.find (fn elem => elem = c) vowels
of
SOME c => true
| NONE => false
end;
fun starts_with_vowel nil = false
| starts_with_vowel (chars: char list as (c::_)) =
is_vowel c;
#+end_src
* 3.3.10
#+name: list_procs
#+begin_src sml
fun take_until pred nil = []
| take_until pred (x::xs) =
if pred x
then []
else x :: (take_until pred xs);
fun drop_until pred nil = []
| drop_until pred (lst as (x::xs)) =
if pred x
then
lst
else
drop_until pred xs;
#+end_src
#+name: string_procs
#+begin_src sml
<>
fun string_take_until char_pred str =
let
val chars = String.explode str;
in
String.implode (take_until char_pred chars)
end;
fun string_drop_until char_pred str =
let
val chars = String.explode str;
in
String.implode (drop_until char_pred chars)
end;
#+end_src
#+begin_src sml
<>
<>
fun pig_latin "" = ""
| pig_latin word =
if starts_with_vowel (String.explode word)
then
word ^ "yay"
else
let
val non_vowel_prefix = string_take_until is_vowel word;
val vowel_and_rest_suffix = string_drop_until is_vowel word;
in
vowel_and_rest_suffix ^
non_vowel_prefix ^
"ay"
end;
#+end_src
* 3.3.11 - representation of sets as single linked lists
#+name: set_procs
#+begin_src sml :noweb yes
fun member (item, nil) = false
| member (item, x::xs) =
item = x orelse member (item, xs);
fun delete (item, nil) = []
| delete (item, x::xs) =
if item = x
then xs
else x :: delete (item, xs);
fun insert (item, nil) = [item]
| insert (item, lst as x::xs) =
if item = x
then lst
else x :: insert (item, xs);
#+end_src
* 3.3.12 - prepend to all sublists
#+begin_src sml
fun prepend_to_all (elem, nil) = []
| prepend_to_all (elem, l::ls) =
(elem :: l) :: (prepend_to_all (elem, ls));
#+end_src
* 3.3.13 - powerset
#+begin_src sml :noweb yes
fun prepend_to_all (elem, nil) = []
| prepend_to_all (elem, l::ls) =
(elem :: l) :: (prepend_to_all (elem, ls));
(* my first working solution *)
fun powerset lst =
let
fun iter (acc, nil) = acc
| iter (acc, remaining as x::xs) =
iter (acc @ (prepend_to_all (x, acc)), xs);
in
iter ([[]], lst)
end;
(* shorter version which the book meant to hint at *)
fun powerset (x::xs) =
prepend_to_all (x, (powerset xs)) @ (powerset xs)
| powerset nil = [[]];
#+end_src
* 3.3.14 - products of differences
#+begin_src sml
fun product_of_differences (a, nil) = a
| product_of_differences (a, lst) =
(* using foldl to do 2 things in one pass over the list *)
foldl (fn (b, accumulated) => accumulated * (a - b))
1.0
lst;
(* not sure what to name this function *)
fun unnamed_func nil = 1.0
| unnamed_func (x::nil) = 1.0
| unnamed_func (x::xs) =
product_of_differences (x, xs);
#+end_src
* 3.3.15 - is empty list?
#+begin_src sml
fun is_empty nil = true
| is_empty _ = false;
#+end_src
#+RESULTS:
: val is_empty = fn : 'a list -> bool
* 3.3.16 - deduction of inference
The given function:
#+begin_src sml
fun sumPairs nil = 0
| sumPairs ((x,y)::zs) = x + y + (sumPairs zs);
#+end_src
+ ML infers the result type from the first pattern, where the result is the integer 0. All pattern matching branches must have the same result type, so the return type of the function must be ~int~.
+ Only integers add up to integers. Reals do only add up to reals and other types do not add up to ~int~ either.
+ To fulfill the condition, that all pattern matching branches must have the same result type, the addition of ~x + y + (sumPairs zs)~ must consist of integers.
+ This means, that ~x~ and ~y~ must be of type ~int~.
+ ~x~ and ~y~ are captured in the pattern ~((x,y)::zs)~.
+ This means, that at least at the beginning of the list, there must be a tuple of ~(x,y)~, which are of type ~int~.
+ The function assumes, that this remains true for all subsequent recursive calls.
+ In ML it is not possible to create a list, which consists of elements of inequal types.
+ If the first element of the list is a 2-tuple of elements of type ~int~, then so must the rest of the list be.
* 3.3.x - split and merge
#+name: split
#+begin_src sml :noweb yes
fun split nil = (nil, nil)
| split [a] = ([a], nil)
| split (a::b::cs) =
let
val (M, N) = split cs;
in
(a::M, b::N)
end;
#+end_src
#+RESULTS:
: val split = fn : 'a list -> 'a list * 'a list
#+name: merge
#+begin_src sml :noweb yes
(* merge 2 already sorted lists *)
fun merge less (nil, M) = M
| merge less (N, nil) = N
| merge less (L1 as x::xs, L2 as y::ys) =
if less (x, y)
then x :: merge less (xs, L2)
else y :: merge less (L1, ys);
#+end_src
#+RESULTS: merge
: val merge = fn : int list * int list -> int list
#+name: merge_sort
#+begin_src sml :noweb yes
<>
<>
fun merge_sort less nil = nil
| merge_sort less [a] = [a]
| merge_sort less lst =
let
(* split list in partitions of elements of alternating indices *)
val (part1, part2) = split lst;
(* sort parts in recursive calls *)
val sorted_part1 = merge_sort less part1;
val sorted_part2 = merge_sort less part2;
in
merge less (sorted_part1, sorted_part2)
end;
#+end_src
#+RESULTS: merge_sort
: val split = fn : 'a list -> 'a list * 'a list
* 3.4.1 - exponentiation function
#+begin_src sml
open IntInf;
fun expt_by_1000 0 = 0
| expt_by_1000 1 = 1
| expt_by_1000 x =
let
fun iter (accumulated, exponent) =
if exponent < 2
then accumulated * x
else iter (accumulated * x, exponent - 1);
in
iter (1, 1000)
end;
#+end_src
* 3.4.2
#+begin_src sml
fun split nil = (nil, nil)
| split [a] = ([a], nil)
| split (a::b::cs) =
let
val left_and_right = split cs;
in
(a::(
#1 left_and_right), b::(
#2 left_and_right))
end;
#+end_src
#+RESULTS:
: val split = fn : 'a list -> 'a list * 'a list
* 3.4.3 - powerset improved
#+begin_src sml
fun prepend_to_all (elem, nil) = []
| prepend_to_all (elem, l::ls) =
(elem :: l) :: (prepend_to_all (elem, ls));
(* shorter version which the book meant to hint at *)
fun powerset nil = [[]]
| powerset (x::xs) =
let
val ps = powerset xs;
in
prepend_to_all (x, ps) @ ps
end;
#+end_src
* 3.4.4 - maximum improved
There is no useful place to put a ~let~ in here:
#+begin_src sml
fun find_largest (lst: real list) =
foldl (fn (elem, prev) => if elem > prev
then elem
else prev)
Real.negInf
lst;
#+end_src
* 3.4.5 - x^(2^i)
#+begin_src sml
fun expt2_expti (x, i) =
let
fun iter (acc, x, 0) = acc
| iter (acc, x, i) =
iter (acc * Math.pow (x, 2.0), x, i-1);
in
iter (1.0, x, i)
end;
expt2_expti (2.0, 4);
#+end_src
* 3.4.6 - alternative sum pairs
#+begin_src sml
fun sum_pairs nil = (0, 0)
| sum_pairs ((x, y)::zs) =
let
val (rest_odds_sum, rest_evens_sum) = sum_pairs zs;
in
(x + rest_odds_sum, y + rest_evens_sum)
end;
#+end_src
* 3.4.7 - sums of even and odd positions
#+begin_src sml
fun sum_even_odd nil = (0, 0)
| sum_even_odd (x::nil) = (x, 0)
| sum_even_odd (a::b::lst) =
let
val (a_next, b_next) = sum_even_odd lst;
in
(a + a_next, b + b_next)
end;
#+end_src
* 3.5.1 - concatenate lists
#+begin_src sml
fun cat (nil, nil) = nil
| cat (nil, lst2) = lst2
| cat (lst1, nil) = lst1
| cat (x::xs, ys) =
x :: cat (xs, ys);
#+end_src
* 3.5.2 - cycle - linear time
#+begin_src sml
(* WITH CONTINUATIONS *)
fun split_cycled_and_remaining (nil, count_to_take) = (nil, nil)
| split_up (lst, count_to_take) =
let
fun iter (nil, count_to_take, collect) = collect (nil, nil) (* PART1: Giving nil to the collector. *)
(* If there are still elements in the list ... *)
| iter ((x::xs), count_to_take, collect) =
(* ... and the list is supposed to be cycled by more elements ... *)
if count_to_take > 0
then
iter (xs,
count_to_take - 1,
(* + Wrap the collector in a new lambda expression, to
make a new collector.
+ The new collector uses the result of the old collector.
+ The old collector is given the arguments of the next
call, so that these are used before the current ones. *)
(fn (next_taken_elem, remaining_elems) =>
(* cons the current element in from of the result
from the previous collector *)
let
val (taken, rest) = collect (next_taken_elem, remaining_elems);
in
(* TODO: List is in wrong order. How to reverse without cost? *)
((x :: taken), rest)
end))
else
collect (nil, x::xs)
in
iter (lst,
count_to_take,
(* Initial continuation / collect function. This one will be the
last to be called. It will be wrapped by other lambdas during
processing the list. It being called last means, that one has
to give it nil, to finish building lists. See PART1.*)
(fn (taken, rest) => (taken, rest)))
end;
fun cycle (lst, 0) = lst
| cycle (nil, count) = nil
| cycle (lst, count) =
let
val (rev_cycle_elems, remaining) = split_cycled_and_remaining (lst, count);
in
remaining @ (rev rev_cycle_elems)
end;
(* OTHER SOLUTION *)
(* I found the following solution online at
, however, without
my comments, it is completely unreadable and ultimately it is not so great,
because it does unnecessary work. Made me lose some time figuring out, whether
this solution has any great insights, but apparently it has none. *)
fun cycle3 (nil, nil, ys) = ys
(* If there are no more reversed elements to be cycled, reverse the reversed
rest of the original list. This seems to be a bit of a silly step to do, as
they were only reversed in phase 2, to now be reversed again. >.<' *)
| cycle3 (x2r::x2rs, nil, ys) =
cycle3 (x2rs, nil, x2r::ys)
(* If there are more reversed elements to be cycled reverse them to correct
order and build up the resulting list. *)
| cycle3 (x2rs, x1r::x1rs, ys) = cycle3 (x2rs, x1rs, x1r::ys)
(* Phase 2: "Collect the rest of the original list."
Called as follows:
(arg 1) remaining elements of original list
(arg 2) initially empty list
(arg 3) accumulated elements, which shall be cycled *)
fun cycle2 (nil, x2rs, x1rs) =
(* If the original list has no more elements, go to phase 3.
Calling as follows:
(arg 1) reversed rest of original list
(arg 2) reversed elements to be cycled
(arg 3) initially empty list *)
cycle3 (x2rs, x1rs, nil)
(* If the original list has more elements, ... *)
| cycle2 (x2::x2s, x2rs, x1rs) =
(* ... accumulate them as well in reversed order, until no more elements are
left. At that point we have runtime of O(n) already. The algorithm cannot
have better worst case time complexity than O(n). *)
cycle2 (x2s, x2::x2rs, x1rs);
(* Phase 1: "Accumulate the elements to be cycled according to the counter and
original list." *)
fun cycle1 (x2s, x1rs, 0) =
(* If the number of to cycle elements has reached zero, enter the second
phase - The result of the first phase is, that all elements, which should
be cycled, are accumulated in reversed order in x1rs. *)
cycle2 (x2s, nil, x1rs)
| cycle1 (x::x2s, x1rs, n) =
(* If there are still elements in the list, which shall be cycled ... *)
(* ... cons the head of the original list onto the accumulated list. This
reverses the order of elements found in the original list. Continue with
the counter reduced by 1. *)
cycle1 (x2s, x::x1rs, n - 1)
| cycle1 (nil, x1rs, n) =
(* If there is no remaining element, but still elements should be cycled,
return the empty list. -- Is this a coorect idea? Why not cycle elements
more than once, if there are insufficient elements? Would that not be a
more natural cycling algorithm? *)
nil;
(*
,* x1s : list of elements to cycle
,* n : number of shifts to make
,*)
fun cycle (x1s, n) =
(* original list goes into the first slot *)
cycle1 (x1s, nil, n);
(* Considering, that I have not found a good solution online, I wonder, what the
actual by the author intended solution was. Did the exercise have 2 exclamation
marks, because of the way one might need to use continuations? Because of how it
involves multiple steps? Or did it have 2 exclamation marks, because the
solution is even more difficult to find, than the one with continuations? *)
#+end_src
* 3.6.* Polynomials
#+begin_src sml
structure Polynomial = struct
type CoefficientsList = real list;
fun add P nil = P
| add nil Q = Q
| add ((p:real)::ps) (q::qs) =
(p + q) :: (add ps qs);
fun scalar_multiply nil scalar = nil
| scalar_multiply ((p:real)::ps) scalar =
(p * scalar) :: (scalar_multiply ps scalar);
(* The coefficients of lowest exponents come first by
convention. Prepending a 0.0 will move all coefficients
to positions of coefficients of the next higher
exponents. *)
fun shift_increasing P = 0.0 :: P;
fun shift P 0 = P
| shift P n = shift_increasing (shift P (n - 1));
fun multiply P nil = nil
| multiply P (q::qs) =
add
(* Start calculating with the coefficient of the
lowest exponent. At the beginning, this will be
the coefficient for x^0. For x^0 only the
coefficient remains, because x^0 = 1. For later
iterations, the result will be shifted by 1
position to account for the higher exponent. *)
(scalar_multiply P q)
(*
The next iteration will create a result for the
next higher exponent. This fact is taken into
account here, by shifting the result of the
recursive call by 1 position, before adding it
to the complete result of the computation.
At each iteration, we increase the shift by 1,
which makes sense, because the list of
coefficients representing a polynomial is
complete from lowest exponent position to
highest exponent position. For example when we
multiply with the coefficient of x^1, the shift
will be 1. Then when we multiply with the
coefficient of x^2 the result will be shifted by
2. As a result we get the coefficients at the
correct positions.
,*)
(shift_increasing
(* Calculate the result for the coefficient
of the next exponent. *)
(multiply P qs));
fun substract P nil = P
| substract P Q =
add P (scalar_multiply Q ~1.0);
fun length P = List.length P;
end;
#+end_src
** Karatsuba-Ofman
There is some mathematical stuff in the book leading to the implementation of Karatsuba-Ofman algorithm.
#+begin_src sml
structure KaratsubaOfman = struct
exception UnsuitablePolynomial;
fun best_split n m =
(* If n is less than or equal to half of m, then use n. (Use the much smaller one.) *)
if 2 * n <= m then n
(* If m is less than or equal to half of n, then use m. (Use the much smaller one.) *)
else if 2 * m <= n then m
(* If n is less or equal to m, take half of m. *)
else if n <= m then m div 2
(* n/2 < m < n*)
else n div 2;
fun carve nil 0 = (nil, nil)
| carve nil n = raise UnsuitablePolynomial
| carve (P: Polynomial.CoefficientsList) 0 = (nil, P)
| carve ((p::ps): Polynomial.CoefficientsList) n =
let
val (qs,rs) = carve ps (n-1);
in
(p::qs, rs)
end;
fun karatsuba_ofman_multiply P nil = nil
| karatsuba_ofman_multiply nil Q = nil
| karatsuba_ofman_multiply P (q::nil) = Polynomial.scalar_multiply P q
| karatsuba_ofman_multiply (p::nil) Q = Polynomial.scalar_multiply Q p
| karatsuba_ofman_multiply P Q =
let
val n = Polynomial.length P;
val m = Polynomial.length Q;
val s = Polynomial.best_split n m;
val (T, U) = carve P s;
val (V, W) = carve Q s;
val TV = karatsuba_ofman_multiply T V;
val UW = karatsuba_ofman_multiply U W;
val TUVW = karatsuba_ofman_multiply (Polynomial.add T U) (Polynomial.add V W);
val middle = Polynomial.substract (Polynomial.substract TUVW TV) UW;
in
Polynomial.add
(Polynomial.add
TV
(Polynomial.shift middle s))
(Polynomial.shift UW (2*s))
end;
end;
#+end_src
** 3.6.1 - genPoly
#+begin_src sml
fun gen_poly 0 = nil
| gen_poly n =
1.0 :: (gen_poly (n - 1));
(* Differences start to be visible when making polynomials with 2000
coefficients. *)
KaratsubaOfman.karatsuba_ofman_multiply (gen_poly 1000) (gen_poly 1000);
Polynomial.multiply (gen_poly 2000) (gen_poly 2000);
#+end_src
** 3.6.2 - use naive multiply for low count of exponents
#+begin_src sml
fun karatsuba_ofman_multiply P nil = nil
| karatsuba_ofman_multiply nil Q = nil
| karatsuba_ofman_multiply P (q::nil) = Polynomial.scalar_multiply P q
| karatsuba_ofman_multiply (p::nil) Q = Polynomial.scalar_multiply Q p
| karatsuba_ofman_multiply P Q =
if (Polynomial.length P) + (Polynomial.length Q) < 4000
then
Polynomial.multiply P Q
else
let
val n = Polynomial.length P;
val m = Polynomial.length Q;
val s = Polynomial.best_split n m;
val (T, U) = carve P s;
val (V, W) = carve Q s;
val TV = karatsuba_ofman_multiply T V;
val UW = karatsuba_ofman_multiply U W;
val TUVW = karatsuba_ofman_multiply (Polynomial.add T U) (Polynomial.add V W);
val middle = Polynomial.substract (Polynomial.substract TUVW TV) UW;
in
Polynomial.add
(Polynomial.add
TV
(Polynomial.shift middle s))
(Polynomial.shift UW (2*s))
end;
#+end_src
** 3.6.3 - eval with value for x
#+begin_src sml
structure Polynomial = struct
type CoefficientsList = real list;
exception UnsuitablePolynomial;
fun add P nil = P
| add nil Q = Q
| add ((p:real)::ps) (q::qs) =
(p + q) :: (add ps qs);
fun scalar_multiply nil scalar = nil
| scalar_multiply ((p:real)::ps) scalar =
(p * scalar) :: (scalar_multiply ps scalar);
(* The coefficients of lowest exponents come first by
convention. Prepending a 0.0 will move all coefficients
to positions of coefficients of the next higher
exponents. *)
fun shift_increasing P = 0.0 :: P;
fun shift P 0 = P
| shift P n = shift_increasing (shift P (n - 1));
fun multiply P nil = nil
| multiply P (q::qs) =
add
(* Start calculating with the coefficient of the
lowest exponent. At the beginning, this will be
the coefficient for x^0. For x^0 only the
coefficient remains, because x^0 = 1. For later
iterations, the result will be shifted by 1
position to account for the higher exponent. *)
(scalar_multiply P q)
(*
The next iteration will create a result for the
next higher exponent. This fact is taken into
account here, by shifting the result of the
recursive call by 1 position, before adding it
to the complete result of the computation.
At each iteration, we increase the shift by 1,
which makes sense, because the list of
coefficients representing a polynomial is
complete from lowest exponent position to
highest exponent position. For example when we
multiply with the coefficient of x^1, the shift
will be 1. Then when we multiply with the
coefficient of x^2 the result will be shifted by
2. As a result we get the coefficients at the
correct positions.
,*)
(shift_increasing
(* Calculate the result for the coefficient
of the next exponent. *)
(multiply P qs));
fun substract P nil = P
| substract P Q =
add P (scalar_multiply Q ~1.0);
fun length P = List.length P;
fun eval nil x = raise UnsuitablePolynomial
| eval P (x:real) =
let
fun iter (nil, x, exponent: int) = 0.0
| iter ((p::ps), x, exponent: int) =
(p * Math.pow(x, (Real.fromInt exponent))) + iter(ps, x, (exponent + 1));
in
iter (P, x, 0)
end;
end;
#+end_src
** 3.6.4 - Polynomial roots
Given a list of coefficients, the polynomial is searched, which is the product of (x - coeff) for each coefficient. I do not understand why that product is the polynomial, whose root the polynomial of those coefficients is. Would not a "root" mean, that the resulting polynomial divided by one of the roots results in exactly the root again? As in square root of a polynomial? Perhaps the word "root" is used in a different sense here? Anyway, the book already gives the solution of how to calculate the "root" and that is what is implemented in the following:
#+begin_src sml
fun from_roots nil = 1.0
| from_roots (root::roots) =
(* Starting from lowest exponent of x, so the scalar is x^0 and x
is x^1, which means the substracted coefficient comes first. *)
Polynomial.multiply [~root, 1.0] (from_roots roots);
#+end_src
** 3.6.5 - Polynomials of Polynomials
This is about using polynomials of another variable (for example y) as coefficients for a polynomial (for example of x). For example:
1 + 2xy + 3xy^2 4(x^3)y
. This term can be written to make it easier to see the coefficients:
1 + 2xy + 3xy^2 + 4(x^3)y
= +1 + 2xy + 3xy^2 + 4(x^3)y | adding the + sign
= +1 + 2 xy + 3 xy^2 + 4(x^3)y | adding space for alignment
= +1(x^0) + 2(x^1)y + 3(x^1)y^2 + 4(x^3)y | adding x^n to make coefficients for the same exponents better visible
Now the polynomials of y become visible:
At position x^0: +1(x^0)
-> We are looking for a polynomial of "y", which results in a term with no more "y" in it.
-> ay^0 would result in a mere number.
-> ay^0 with value for "a" must result in 1.
Any value ^0 is 1, so that 1 needs to be multiplied with 1 to result in a 1.
-> The coefficient then is represented by: ~[1.0]~.
At position x^1: + 2(x^1)y + 3(x^1)y^2
-> We can take the x^1 outside: x^1 * (+2y +3y^2). The "y" containing term can be written completely as:
0y^0 + 2y^1 + 3y^2
-> The coefficient then is represented by: ~[0.0, 2.0, 3.0]~.
At position x^2: no term.
-> The coefficient then is represented by: ~[]~.
At position x^3: +4(x^3)y
The "y" containing term is +4y. It can be written completely as:
0y^0 +4y^1
-> The coefficient then is represented by: ~[0.0, 4.0]~.
#+begin_src sml
structure PolyPolynomial = struct
type CoeffList = real list;
type CoeffListList = CoeffList list;
exception UnsuitablePolyPolynomial;
fun add nil nil = nil
| add (PP: CoeffListList) nil = PP
| add nil (QQ: CoeffListList) = QQ
| add ((P:CoeffList)::Ps) ((Q:CoeffList)::Qs) =
(Polynomial.add P Q) :: (add Ps Qs);
(* Multiply a poly-polynomial with a polynomial. It is the
corresponding function to scalar multiplication for
polynomials. *)
fun polynomial_multiply nil polynomial = nil
| polynomial_multiply (P::Ps) polynomial =
(Polynomial.multiply P polynomial) :: (polynomial_multiply Ps polynomial);
(* Corresponding function to shift_increasing of
Polynomial. Instead of shifting by prepending a 0.0 to a list, we
shift by prepending nil, a list, to the list of lists of
coefficients.*)
fun shift_increasing PP = [] :: PP;
(* The function multiply is the same as multiply for
polynomials. Except that everything is one abstraction layer
higher. Instead of multiplying with a number as a coefficient, we multiply with *)
fun multiply nil nil = nil
| multiply PP nil = nil
| multiply nil QQ = nil
| multiply PP (Q::Qs) =
add
(polynomial_multiply PP Q)
(shift_increasing (multiply PP Qs));
end;
#+end_src
* TODO 4.
* Other code
:PROPERTIES:
:CUSTOM_ID: other-code
:END:
Perhaps this code should be put in a separate module.
** n choose k - summing smaller problems
#+begin_src sml
open IntInf;
fun n_choose_k (n: int, 1) = n
| n_choose_k (n: int, k: int) =
let
fun iter (n: int, k: int) =
if k = 0 orelse n = k
then 1
else
(* the number of possibilities without choosing one of the n
elements *)
iter (n - 1, k)
+
(* the number of possibilities with choosing one of the n
elements *)
iter (n - 1, k - 1);
in
if n >= k
then
iter (n, k)
else
raise Fail "n must be greater than or equal to k"
end;
#+end_src
** n choose k
#+begin_src sml
open IntInf;
fun n_choose_k (n: int, k: int) =
let
fun fac 0 = 1
| fac 1 = 1
| fac n =
let
fun iter 1 prod = prod
| iter remaining_factors prod =
iter (remaining_factors - 1) (prod * remaining_factors);
in
if n > 0
then iter n 1
else raise Fail "n must be 0 or greater"
end;
in
(fac n) div ((fac (n - k)) * (fac k))
end;
#+end_src
** reverse list
#+begin_src sml
fun reverse L =
let
fun iter (nil, M) = M
| iter (x::xs, ys) = iter (xs, x::ys)
in
iter (L, nil)
end;
#+end_src
** Printing
#+begin_src sml
fun print_list elem_2_str lst =
let
fun iter remaining_lst =
if (null remaining_lst)
then print ""
else
let
val _ = print (elem_2_str (hd remaining_lst));
val _ = print "\n";
in
iter (tl remaining_lst)
end;
in
iter lst
end;
print_list Int.toString [1,2,3,4]
#+end_src