...

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 }