I have the following recursive function that creates a list of 0s (i.e. [0,...,0]) in VDM. How can this be translated to Isabelle using fun-where?
VDM:
NewList: nat1 * seq of nat -> seq of nat
NewList(n, l) ==
if len l = n then l
else NewList(n, l ^ [0])
-- pre/post-conditions excluded here
My attempts are horribly wrong due to my lack of understanding of Isabelle (but below at least proves that I tried...).
Isabelle:
fun
NewList:: "N ⇒ (VDMNat VDMSeq) ⇒ (VDMNat VDMSeq)"
where
"NewList n [] = NewList n [0]"
| "NewList n [x] = (if len [x] = n then [x] else NewList n (x#[0]))"
| "NewList n (x # xs) = (if len (x # xs) = n then (x # xs) else NewList n ((x # xs) # [(0::VDMNat)]))"
*The data types VDMNat and VDMSeq are defined in some library. Please ignore the VDMNat and VDMSeq for now - any sort of implementation using Isabelle's data types are welcome (at least it would provide a good reference for my implementation). Please refer to the VDM code for the data types intended.
Could you also please explain what x, xs, and (x # xs) are referring to? I've seen this in several recursive function examples (though none helps me).
Thank you for your help!
First of all, x and xs are variables. When definiting recursive functions on lists, these are often used to denote the first element of the list (x) and the remaining list (xs). The expression x # xs means ‘x prepended to the list xs’, and that is the reason why (x # xs) # [0] in your question does not work: x # xs is a list and [0] is also a list. You would have to do x # xs # [0}, where # is the function to concatenate two lists.
Now, to your function: My interpretation of your function definition is that you have a natural number n and a list l and want to pad the list l with zeros at the back up to length n.
However, when the list l is of length > n to begin with, your function does not terminate. You would have to think about what to do in that case.
Here are my suggestions for what you could do:
Possibility 1
Change the = n to a ≥ n. Then you can prove termination of the function by looking at
function new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = (if length l ≥ n then l else new_list n (l # [0]))"
by pat_completeness auto
termination by (relation "measure (λ(n, l). n - length l)") auto
However, proving theorems about this will probably get ugly. I would therefore urge you to do something like the following two possibilities. Ideally, use functions from Isabelle's standard library, because there is usually good automation setup for them. Alternatively, define your own small building blocks (like take and replicate) for your datatypes and prove reusable facts on them and combine them to do what you want. A ‘monolithic’ function definition like yours is difficult to work with when doing proofs.
Possibility 2
Use the builtin function replicate, which takes a natural number n and an element and returns a list of n times that element:
definition new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = l # replicate (n - length l) 0"
You can also do the same thing with fun, but definition is the more low-level tool. Note that definition does not add the function definition theorem new_list_def as a simplifier rule; you can do this by writing declare new_list_def [simp].
Possibility 3
You can combine possibility 2 with the builtin function take to ensure that you always get a list of length exactly n, even when the input list is longer (it is then possibly truncated):
definition new_list :: "nat ⇒ nat list ⇒ nat list" where
"new_list n l = take n l # replicate (n - length l) 0"
Summary
In the first two cases, you can prove the theorems
length l ≤ n ⟹ length (new_list n l) = n
take (length l) (new_list n l) = l
(in the first case by induction using new_list.induct; in the second case just by unfolding the definition and simplifying)
In the third case, you can prove
length (new_list n l) = n
take (length l) (new_list n l) = take n l
Obviously, if length l ≤ n, the first two and the last one coincide completely.
The easy solution is: replicate n (0::nat) using the function replicate of Isabelle/HOL's library.
If you want to implement the function yourself via fun then do what you should always do in functional programming ;) try to split your problem into smaller problems that can be solved recursively:
fun newlist :: "nat => nat list"
where
"newlist 0 = []" -- "the only list of length 0*)
| "newlist (Suc n) = ..." -- "use result for 'n' to obtain result for 'n+1'"
Related
I'm trying to self-learn some programming in a functional programming language and recently stumbled on the problem of generating all the permutations of length m from a list of length n, with repetition. Mathematically, this should result in a total of n^m possible permutations, because each of the m 'slots' can be filled with any of the n elements. The code I have currently, however, does not give me all the elements:
let rec permuts n list =
match n, list with
0, _ -> [[]]
| _, [] -> []
| n, h :: t -> (List.map (fun tt -> h::tt) (permuts (n-1) list))
# permuts n t;;
The algorithm basically takes one element out of a list with m elements, slaps it onto the front of all the combinations with the rest of the elements, and concatenates the results into one list, giving only n C m results.
For example, the output for permuts 2 [1;2;3] yields
[[1;1]; [1;2]; [1;3]; [2;2]; [2;3]; [3;3]]
whereas I actually want
[[1;1]; [1;2]; [1;3]; [2;1]; [2;2]; [2;3]; [3;1]; [3;2]; [3;3]]
-- a total of 9 elements. How do I fix my code so that I get the result I need? Any guidance is appreciated.
Your error appears on the second line of:
| n, h :: t -> List.map (fun tt -> h::tt) (permuts (n-1) list)
# permuts n t
Indeed, with this you are decomposing the set of n-tuples with k elements as the sum of
the set of (n-1)-tuples prefixed with the first element
the set of n-tuples with (k-1) elements
Looking at the cardinal of the three sets, there is an obvious mismatch since
k^n ≠ k^(n-1) + (k-1)^n
And the problem is that the second term doesn't fit.
To avoid this issue, it is probably better to write a couple of helper function.
I would suggest to write the following three helper functions:
val distribute: 'a list -> 'a list -> 'a list list
(** distribute [x_1;...;x_n] y returns [x_1::y;...x_n::y] *)
val distribute_on_all: 'a list -> 'a list list
(** distribute_on_all x [l_1;...;l_n] returns distribute x l_1 # ... # distribute x l_n *)
val repeat: int -> ('a -> 'a) -> 'a -> 'a
(** repeat n f x is f(...(f x)...) with f applied n times *)
then your function will be simply
let power n l = repeat n (distribute_on_all l) [[]]
In Haskell, it's very natural to do this using a list comprehension:
samples :: Int -> [a] -> [[a]]
samples 0 _ = [[]]
samples n xs =
[ p : ps
| p <- xs
, ps <- samples (n - 1) xs
]
It seems to me you never want to recurse on the tail of the list, since all your selections are from the whole list.
The Haskell code of #dfeuer looks right. Note that it never deconstructs the list xs. It just recurses on n.
You should be able to copy the Haskell code using List.map in place of the first two lines of the list comprehension, and a recursive call with (n - 1) in place of the next line.
Here's how I would write it in OCaml:
let perm src =
let rec extend remaining_count tails =
match remaining_count with
| 0 -> tails
| _ ->
(* Put an element 'src_elt' taken from all the possible elements 'src'
in front of each possible tail 'tail' taken from 'tails',
resulting in 'new_tails'. The elements of 'new_tails' are one
item longer than the elements of 'tails'. *)
let new_tails =
List.fold_left (fun new_tails src_elt ->
List.fold_left (fun new_tails tail ->
(src_elt :: tail) :: new_tails
) new_tails tails
) [] src
in
extend (remaining_count - 1) new_tails
in
extend (List.length src) [[]]
The List.fold_left calls may look a bit intimidating but they work well. So it's a good idea to practice using List.fold_left. Similarly, Hashtbl.fold is also common and idiomatic, and you'd use it to collect the keys and values of a hash table.
How to define a recursive function in the (pure) calculus of constructions? I do not see any fixpoint combinator there.
People in the CS stack exchange might be able to provide some more insight, but here is an attempt.
Inductive data types are defined in the calculus of constructions with a Church encoding: the data type is the type of its fold function. The most basic example are the natural numbers, which are defined as follows, using a Coq-like notation:
nat := forall (T : Type), T -> (T -> T) -> T
This encoding yields two things: (1) terms zero : nat and succ : nat -> nat for constructing natural numbers, and (2) an operator nat_rec for writing recursive functions.
zero : nat
zero T x f := x
succ : nat -> nat
succ n T x f := f (n T x f)
nat_rec : forall T, T -> (T -> T) -> nat -> T
nat_rec T x f n := n T x f
If we pose F := nat_rec T x f for terms x : T and f : T -> T, we see that the following equations are valid:
F zero = x
F (succ n) = f (F n)
Thus, nat_rec allows us to define recursive functions by specifying a return value x for the base case, and a function f to process the value of the recursive call. Note that this does not allow us to define arbitrary recursive functions on the natural numbers, but only those that perform recursive calls on the predecessor of their argument. Allowing arbitrary recursion would open the door to partial functions, which would compromise the soundness of the calculus.
This example can be generalized to other inductive data types. For instance, we can define the type of lists of natural numbers as the type of their fold right function:
list_nat := forall T, T -> (nat -> T -> T) -> T
I am new to Isabelle and I am trying to define primitive recursive functions. I have tried out addition but I am having trouble with multiplication.
datatype nati = Zero | Suc nati
primrec add :: "nati ⇒ nati ⇒ nati" where
"add Zero n = n" |
"add (Suc m) n = Suc(add m n)"
primrec mult :: "nati ⇒ nati ⇒ nati" where
"mult Suc(Zero) n = n" |
"mult (Suc m) n = add((mult m n) m)"
I get the following error for the above code
Type unification failed: Clash of types "_ ⇒ _" and "nati"
Type error in application: operator not of function type
Operator: mult m n :: nati
Operand: m :: nati
Any ideas?
The problem is your mult function: It should look like this:
primrec mult :: "nati ⇒ nati ⇒ nati" where
"mult Zero n = Zero" |
"mult (Suc m) n = add (mult m n) m"
Function application in functional programming/Lambda calculus is the operation that binds strongest and it associates to the left: something like f x y means ‘f applied to x, and the result applied to y’ – or, equivalently due to Currying: the function f applied to the parameters x and y.
Therefore, something like mult Suc(Zero) n would be read as mult Suc Zero n, i.e. the function mult would have to be a function taking three parameters, namely Suc, Zero, and n. That gives you a type error. Similarly, add ((mult m n) m) does not work, since that is identical to add (mult m n m), which would mean that add is a function taking one parameter and mult is one taking three.
Lastly, if you fix all that, you will get another error saying you have a non-primitive pattern on the left-hand side of your mult function. You cannot pattern-match on something like Suc Zero since it is not a primitive pattern. You can do that if you use fun instead of primrec, but it is not what you want to do here: You want to instead handle the cases Zero and Suc (see my solution). In your definition, mult Zero n would even be undefined.
This is related to Project Euler problem 523.
The following algorithm sorts a list:
Starting from the beginning of the list, check each pair of adjacent elements in turn.
If the elements are out of order:
Move the smallest element of the pair at the beginning of the list.
Restart the process from step 1.
If all pairs are in order, stop.
I am attempting (for practice) to code this up in Isabelle. I have obtained the following function definitions:
fun firstpos :: "nat list ⇒ nat"
where "firstpos [] = 0"
| "firstpos (x # y # xs) = (if (x > y) then 1 else (firstpos (y#xs))+1)"
| "firstpos [x] = Suc 0"
and
fun insertAtFront :: "(nat list) ⇒ (nat) ⇒ (nat list)"
where "insertAtFront l 0 = l"
| "insertAtFront l i = [hd (rev (take i l))] # (take (i-1) l) # (rev (take ((length l) - i) (rev l)))"
where firstpos determines the list-index of the first element which is greater than its successor in the list, and insertAtFront permutes the list so that the "second argument"th element is at the front.
That is, firstPos effectively tells us where to start doing Step 2 of the algorithm, and insertAtFront is step 2.1 of the algorithm.
Now, I wish to define the function which is basically the fixed point of the composition of these. In other words,
fun sortproc :: "nat list ⇒ nat list"
where "sortproc l = sortproc (insertAtFront l (firstpos l + 1))"
It is nontrivial that this procedure ever terminates, but I can prove it mathematically: at each stage, $\sum_{i=1}^n 2^i L_{n-i}$ decreases, where $n$ is the length of list $L$.
I am a novice in Isabelle. How can I code the idea "this definition is well-defined, because of this proof" in Isabelle? The documentation seems to talk mainly about size arguments, and the size of the input isn't changing: it's always a list of the same length.
I'm just getting started in Isabelle and I'm getting a type unification error while working through Exercise 3.3 in of Concrete Semantics:
Define a substitution function
subst :: vname ⇒ aexp ⇒ aexp ⇒ aexp
such that subst x a e is the result of replacing every occurrence of variable x by a in e. For example:
subst ''x'' (N 3) (Plus (V ''x'') (V ''y'')) = Plus (N 3) (V ''y'')
Here's what I've got so far:
theory Scratchpad
imports Main
begin
type_synonym vname = string
type_synonym val = int
type_synonym state = "vname ⇒ val"
datatype aexp = N int | V vname | Plus aexp aexp
fun subst :: "vname ⇒ aexp ⇒ aexp ⇒ aexp" where
"subst x (N a) (N e) = (N e)" |
"subst x (N a) (V e) = (if x=e then (N a) else (V e))" |
"subst x (N a) (Plus e1 e2) = Plus(subst(x (N a) e1) subst(x (N a) e2))"
end
When the third case in the function definition is commented out, running the test cases
value "subst ''x'' (N 3) (N 5)"
value "subst ''x'' (N 3) (V ''x'')"
produces (N 5) and (N 3) respectively, so I know the first two lines are working correctly. Adding the last line results in the error
Type unification failed: Clash of types "_ ⇒ _" and "_ list"
Type error in application: operator not of function type
Operator: x :: char list
Operand: N a :: aexp
I don't think this is a syntax issue, although I'm not yet completely sure what purposes different types of quotation marks serve (e.g. double quotes vs. two single quotes). From this answer, I believe that Isabelle is assigning x to be a function type on the right side of the line, which is not what I want.
What do the error messages actually mean (specifically and generally), and how do I fix this?
To answer your question about quotes: Two single quotes are used in Isabelle/HOL (more precisely its inner syntax) to denote string literals. That is, by ''abc'' we denote the string containing the three characters a, b, and c (which would again use some special syntax if you had to enter them literally). Double quotes on the other hand, are mostly used to separate Isar statements (outer syntax) from terms inside the logic. So while ''...'' is part of the term language, "..." is not.
Now for the error message. It tells you that you are trying to use the list x (type _ list) as a function (type _ => _). Why does Isabelle think you want to use x as a function? Well, because juxtaposition (i.e., writing terms next to each other, separated by white space) denotes function application. Thus x (N a) is interpreted as applying a function x to an argument (N a) (just as f y is the application of f to the argument y). In order to give your definition the correct meaning you have to use parenthesis at the right positions. I guess what you intended in your third clause was:
Plus (subst x (N a) e1) (subst x (N a) e2)
where we have two occurrences of the function subst applied to three arguments. (So it was a syntax issue after all ;).)
Another comment. Your implementation of subst could be more general. As is, the second argument of subst is always fixed to be some number a (because of your usage of the constructor N). However, everything should work just as well if you would allow arbitrary expressions of type aexp.