...
 
Commits (2)
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assignment 7.1 (P) The List Module (part 2)\n",
"\n",
"In order to write short and easy to understand programs, it is crucial to use existing library functions for regularly appearing problem patterns. The most frequently used library functions are clearly those of the `List` Module and the following in particular. Check the documentation for their types and functionality:\n",
"\n",
"* `List.map`\n",
"* `List.fold_left`\n",
"* `List.fold_right`\n",
"* `List.find_opt`\n",
"* `List.filter`\n",
" \n",
"Implement the following functions without using any recursive functions:\n",
"\n",
"1) `squaresum : int list -> int` computes $\\sum_{i=1}^{n} x_i^2$ for a list $[x_1, \\dotsc, x_n]$.\n",
"\n",
"2) `float_list : int list -> float list` converts all ints in the list to floats.\n",
"\n",
"3) `to_string : int list -> string` builds a string representation of the given list.\n",
"\n",
"4) `part_even : int list -> int list` partitions all even values to the front of the list."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"let squaresum l = (* TODO *)\n",
"\n",
"let float_list l = (* TODO *)\n",
"\n",
"let to_string l = (* TODO *)\n",
" \n",
"let part_even l = (* TODO *)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assignment 7.2 (P) Mappings\n",
"\n",
"There is a need to represent and modify a mapping from one set of values to another set of values in many applications. Remember how we stored the grades in the student record. Storing a list of pairs `'a * 'b` is a simple way to represent a mapping from type `'a` to type `'b`. This kind of list is typically referred to as an associative list:\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1) Implement these functions to work with mappings based on associative lists:\n",
"* `is_empty : ('k * 'v) list -> bool`\n",
"* `get : 'k -> ('k * 'v) list -> 'v option`\n",
"* `put : 'k -> 'v -> ('k * 'v) list -> ('k * 'v) list`\n",
"* `contains_key : 'k -> ('k * 'v) list -> bool`\n",
"* `remove : 'k -> ('k * 'v) list -> ('k * 'v) list`\n",
"* `keys : ('k * 'v) list -> 'k list`\n",
"* `values : ('k * 'v) list -> 'v list`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"let is_empty m = (* TODO *)\n",
"\n",
"let rec get k = (* TODO *)\n",
"\n",
"let put k v m = (* TODO *)\n",
"\n",
"let contains_key k m = (* TODO *)\n",
"\n",
"let rec remove k = (* TODO *)\n",
"\n",
"let keys m = (* TODO *)\n",
"\n",
"let values m = (* TODO *)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2) Check the `List` module for functions that already provide (some of) these functionalities."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An alternative to associative lists is to use functions of type `'k -> 'v option` directly. So for example, the function `fun x -> x * x + 1` respresents a very efficient mapping from any number to the successor of its square.\n",
"\n",
"3) Implement the above functions again for mappings based on functions. Some of these functions cannot be implemented, however:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"let is_empty m = (* TODO *)\n",
"\n",
"let get k m = (* TODO *)\n",
"\n",
"let put k v m = (* TODO *)\n",
"\n",
"let contains_key k m = (* TODO *)\n",
"\n",
"let remove k m = (* TODO *)\n",
"\n",
"let keys m = (* TODO *)\n",
"\n",
"let values m = (* TODO *)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"4) Discuss: What are the advantages of either approach? When would you use which?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assignment 7.3 (P) Operator Functions\n",
"\n",
"In OCaml, infix notation of operators is just syntactic sugar for a call to the corresponding function. The binary addition `+` merely calls the function `(+) : int -> int -> int`.\n",
"\n",
"1) Discuss why this is a very useful feature."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "OCaml 4.07.0",
"language": "OCaml",
"name": "ocaml-jupyter"
},
"language_info": {
"codemirror_mode": "text/x-ocaml",
"file_extension": ".ml",
"mimetype": "text/x-ocaml",
"name": "OCaml",
"nbconverter_exporter": null,
"pygments_lexer": "OCaml",
"version": "4.07.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assignment 7.1 (P) The List Module (part 2)\n",
"\n",
"In order to write short and easy to understand programs, it is crucial to use existing library functions for regularly appearing problem patterns. The most frequently used library functions are clearly those of the `List` Module and the following in particular. Check the documentation for their types and functionality:\n",
"\n",
"* `List.map : ('a -> 'b) -> 'a list -> 'b list` \n",
" Transforms all elements in the list with the given function.\n",
"* `List.fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a`\n",
" Step by step combines all elements in the list from left to right with an initial value.\n",
"* `List.fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b`\n",
" Step by step combines all elements in the list from right to left with an initial value.\n",
"* `List.find_opt : ('a -> bool) -> 'a list -> 'a option`\n",
" Searches the list for an element for which the given function returns true. If such an element `x` is found, it is returned as `Some x`, otherwise `None` is returned.\n",
"* `List.filter : ('a -> bool) -> 'a list -> 'a list`\n",
" Constructs a new list, containing only the elements for which the function returns true.\n",
" \n",
"Implement the following functions without using any recursive functions:\n",
"\n",
"1) `squaresum : int list -> int` computes $\\sum_{i=1}^{n} x_i^2$ for a list $[x_1, \\dotsc, x_n]$.\n",
"\n",
"2) `float_list : int list -> float list` converts all ints in the list to floats.\n",
"\n",
"3) `to_string : int list -> string` builds a string representation of the given list.\n",
"\n",
"4) `part_even : int list -> int list` partitions all even values to the front of the list."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"let squaresum l = List.fold_left (fun a x -> a + x * x) 0 l\n",
"\n",
"let float_list l = List.map float_of_int l\n",
"\n",
"let to_string l = \n",
" \"[\" ^ (List.fold_left (fun a x -> a ^ (string_of_int x) ^ \";\") \"\" l) ^ \"]\"\n",
" \n",
"let part_even l = \n",
" let even = List.filter (fun x -> x mod 2 = 0) l in\n",
" let odd = List.filter (fun x -> x mod 2 <> 0) l in\n",
" even @ odd\n",
"(* or better:\n",
" List.partition (fun x -> x mod 2 = 0) l\n",
"*)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assignment 7.2 (P) Mappings\n",
"\n",
"There is a need to represent and modify a mapping from one set of values to another set of values in many applications. Remember how we stored the grades in the student record. Storing a list of pairs `'a * 'b` is a simple way to represent a mapping from type `'a` to type `'b`. This kind of list is typically referred to as an associative list:\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"1) Implement these functions to work with mappings based on associative lists:\n",
"* `is_empty : ('k * 'v) list -> bool`\n",
"* `get : 'k -> ('k * 'v) list -> 'v option`\n",
"* `put : 'k -> 'v -> ('k * 'v) list -> ('k * 'v) list`\n",
"* `contains_key : 'k -> ('k * 'v) list -> bool`\n",
"* `remove : 'k -> ('k * 'v) list -> ('k * 'v) list`\n",
"* `keys : ('k * 'v) list -> 'k list`\n",
"* `values : ('k * 'v) list -> 'v list`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"let is_empty m = m = empty\n",
"\n",
"let rec get k = function [] -> None \n",
" | (k',v')::ms -> if k' = k then Some v' else get k ms\n",
"\n",
"let put k v m = (k,v)::m\n",
"(* or better: \n",
"let put k v m = (k,v)::remove k m\n",
"*)\n",
"\n",
"let contains_key k m = (get k m) <> None\n",
"\n",
"let rec remove k = function [] -> [] \n",
" | (k',v')::ms -> if k' = k then ms else (k',v')::remove k ms\n",
"\n",
"let keys m = List.map fst m\n",
"\n",
"let values m = List.map snd m"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2) Check the `List` module for functions that already provide (some of) these functionalities.\n",
"\n",
"* `assoc_opt` is `get`\n",
"* `mem_assoc` is `contains_key`\n",
"* `remove_assoc` is `remove`\n",
"* `split` followed by `fst` is `keys`\n",
"* `split` followed by `snd` is `values`\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An alternative to associative lists is to use functions of type `'k -> 'v option` directly. So for example, the function `fun x -> x * x + 1` respresents a very efficient mapping from any number to the successor of its square.\n",
"\n",
"3) Implement the above functions again for mappings based on functions. Some of these functions cannot be implemented, however:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"let is_empty m = failwith \"impossible\"\n",
"\n",
"let get k m = m k\n",
"\n",
"let put k v m = fun x -> if x = k then Some v else m x\n",
"\n",
"let contains_key k m = (get k m) <> None\n",
"\n",
"let remove k m = fun x -> if x = k then None else m x\n",
"\n",
"let keys m = failwith \"impossible\"\n",
"\n",
"let values m = failwith \"impossible\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"4) Discuss: What are the advantages of either approach? When would you use which?\n",
"\n",
"The function approach is very efficient when the mapping is changed rarely or not at all, because the chain of nested function calls grows with every modification. Querying values from the list is linear in the length of the list, however, if mappings change a lot, it is still much better than the other approach or if functionality like `keys` or `values` is required, which the function implementation could only do by iterating over all values in the domain type (e.g. from `min_int` to `max_int` for `'k = int`)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assignment 7.3 (P) Operator Functions\n",
"\n",
"In OCaml, infix notation of operators is just syntactic sugar for a call to the corresponding function. The binary addition `+` merely calls the function `(+) : int -> int -> int`.\n",
"\n",
"1) Discuss why this is a very useful feature.\n",
"\n",
"First, you can define your own operator functions and thus add additional infix operators or change the semantics of an existing one. See the `(=.)` operator defined in the homework tests for example. Second, operators can be used directy in contexts where a function is required. Instead of defining a new function `fun a b -> a + b` the operator `(+)` can be used when folding over a list: `fold_left (+) 0 l`."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "OCaml 4.07.0",
"language": "OCaml",
"name": "ocaml-jupyter"
},
"language_info": {
"codemirror_mode": "text/x-ocaml",
"file_extension": ".ml",
"mimetype": "text/x-ocaml",
"name": "OCaml",
"nbconverter_exporter": null,
"pygments_lexer": "OCaml",
"version": "4.07.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}