Standard ML: Simplifying Recursive Calls - functional-programming

My book has the following definition of inorder traversal (it computes a list with the elements of the tree in the inorder order within a list:
fun trav Empty = []
| trav(Node(t_1, x, t_2)) = trav t_1 # (x::trav t_2);
What's the convention / standard for simplifying the calls in the second line (namely, trav t_1 and x::trav t_2)? I know I simplify both of them before making use of the # operator but I'd like to know whether the first trav call evaluates completely before the other call, vice versa (unlikely), or both simultaneously.
Thanks
bclayman

Your intuition is correct, trav t_1 gets evaluated first as function arguments are evaluated in left to right order. This might seem a little strange, since # is an infix operator, but [1, 2, 3] # [4, 5, 6] can actually be rewritten as (op #)([1, 2, 3], [4, 5, 6]). You can verify that # evaluates its left argument first by doing:
Standard ML of New Jersey v110.78 [built: Sun Jun 7 20:21:33 2015]
- (print "test1\n"; [1, 2, 3]) # (print "test2\n"; [4, 5, 6]);
test1
test2
val it = [1,2,3,4,5,6] : int list
-
Essentially what you have is equivalent to:
fun trav Empty = []
| trav(Node(t_1, x, t_2)) =
let val l = trav t_1
val r = trav t_2
in l # (x::r) end

Related

Making a function to build a particular set of combinations

I'm trying to make a function f such that
f(1) == ((1,),)
f(2) == ((1,), (2,), (1,2))
f(3) == ((1,), (2,), (3,), (1,2), (1,3), (2,3), (1,2,3))
f(4) == ((1,), (2,), (3,), (4,), (1,2), (1,3), (1,4), (2,3), (2,4), (3,4), (1,2,3), (1,2,4), (1,3,4), (2,3,4), (1,2,3,4))
and so on. Anyone have any clever ideas on how to generate this programmatically? I'm sure there's some fancy name for this operation but I'm not sure what it is.
The combinatorics package has this:
using Combinatorics
combinations(1:n) # iterator of all combinations
For example
julia> collect(combinations(1:3))
7-element Array{Array{Int64,1},1}:
[1]
[2]
[3]
[1, 2]
[1, 3]
[2, 3]
[1, 2, 3]
Note that combinations is an iterator, you can use it in a for loop
for c in combinations(1:n)
...
end
without creating all combinations in memory at once (you only create them if you collect the iterator). combinations returns a Vector instead of a tuple so that the type of c does not change from iteration to iteration.
There is some additional information at https://discourse.julialang.org/t/generate-all-subsets-of-a-set/12810/10.
Two answers which were suggested to me on the julialang slack:
using Combinatorics
f(n) = unique(sort.(vcat([collect(permutations(1:n, i)) for i in 1:n]...)))
jointuple(x,y) = (x...,y...)
function f(x)
if x == 0
[]
elseif x == 1
[(1,);]
else
a = f(x-1)
vcat(a,(x,),map(z->jointuple(z,x),a))
end
end

Prolog: display n-th element of list

Using Prolog:
Write a predicate dispnth to display the nth element of a list. You may assume that the input list always has n or more elements.
For Example:
?- dispnth([1, [2, 3], 4, 5], 2, X). should return X = [2, 3]
I have this so far:
dispnth([X|_], 0, X).
dispnth([_|Xs], N, X) :-
dispnth(N1, X, Xs),
N is N1 + 1.
First let's give the predicate a more descriptive name, say list_nth_element/3. Next you might like to consider an auxiliary predicate list_nth_element_/4 with an additional argument, that holds the current position. From your given example I assume that you start counting at 1, so that's going to be the start value for the fourth argument. Then the predicates might look something like this:
list_nth_element(L,N,E) :-
list_nth_element_(L,N,E,1).
list_nth_element_([X|Xs],N,X,N). % if the 2nd and 4th elements are equal X is the nth element
list_nth_element_([_X|Xs],N,E,P0) :- % if the 2nd and 4th arguments
dif(P0,N), % differ
P1 is P0+1, % increment current position
list_nth_element_(Xs,N,E,P1). % and recurse
So essentially the fourth argument is used as a position indicator that is being incremented until you reached the desired position. However, there is no need to have this additional argument in the actual predicates interface, so it is "hidden" in the auxiliary predicate's interface.
Querying this predicate yields your desired result:
?- list_nth_element([1, [2, 3], 4, 5], 2, X).
X = [2,3] ? ;
no
You can also ask things like Which element is at what position?
?- list_nth_element([1, [2, 3], 4, 5], N, X).
N = X = 1 ? ;
N = 2,
X = [2,3] ? ;
N = 3,
X = 4 ? ;
N = 4,
X = 5 ? ;
no

Julia: All possible sums of `n` entries of a Vector with unique integers, (with repetition)

Let's say I have a vector of unique integers, for example [1, 2, 6, 4] (sorting doesn't really matter).
Given some n, I want to get all possible values of summing n elements of the set, including summing an element with itself. It is important that the list I get is exhaustive.
For example, for n = 1 I get the original set.
For n = 2 I should get all values of summing 1 with all other elements, 2 with all others etc. Some kind of memory is also required, in the sense that I have to know from which entries of the original set did the sum I am facing come from.
For a given, specific n, I know how to solve the problem. I want a concise way of being able to solve it for any n.
EDIT: This question is for Julia 0.7 and above...
This is a typical task where you can use a dictionary in a recursive function (I am annotating types for clarity):
function nsum!(x::Vector{Int}, n::Int, d=Dict{Int,Set{Vector{Int}}},
prefix::Vector{Int}=Int[])
if n == 1
for v in x
seq = [prefix; v]
s = sum(seq)
if haskey(d, s)
push!(d[s], sort!(seq))
else
d[s] = Set([sort!(seq)])
end
end
else
for v in x
nsum!(x, n-1, d, [prefix; v])
end
end
end
function genres(x::Vector{Int}, n::Int)
n < 1 && error("n must be positive")
d = Dict{Int, Set{Vector{Int}}}()
nsum!(x, n, d)
d
end
Now you can use it e.g.
julia> genres([1, 2, 4, 6], 3)
Dict{Int64,Set{Array{Int64,1}}} with 14 entries:
16 => Set(Array{Int64,1}[[4, 6, 6]])
11 => Set(Array{Int64,1}[[1, 4, 6]])
7 => Set(Array{Int64,1}[[1, 2, 4]])
9 => Set(Array{Int64,1}[[1, 4, 4], [1, 2, 6]])
10 => Set(Array{Int64,1}[[2, 4, 4], [2, 2, 6]])
8 => Set(Array{Int64,1}[[2, 2, 4], [1, 1, 6]])
6 => Set(Array{Int64,1}[[2, 2, 2], [1, 1, 4]])
4 => Set(Array{Int64,1}[[1, 1, 2]])
3 => Set(Array{Int64,1}[[1, 1, 1]])
5 => Set(Array{Int64,1}[[1, 2, 2]])
13 => Set(Array{Int64,1}[[1, 6, 6]])
14 => Set(Array{Int64,1}[[4, 4, 6], [2, 6, 6]])
12 => Set(Array{Int64,1}[[4, 4, 4], [2, 4, 6]])
18 => Set(Array{Int64,1}[[6, 6, 6]])
EDIT: In the code I use sort! and Set to avoid duplicate entries (remove them if you want duplicates). Also you could keep track how far in the index on vector x in the loop you reached in outer recursive calls to avoid generating duplicates at all, which would speed up the procedure.
I want a concise way of being able to solve it for any n.
Here is a concise solution using IterTools.jl:
Julia 0.6
using IterTools
n = 3
summands = [1, 2, 6, 4]
myresult = map(x -> (sum(x), x), reduce((x1, x2) -> vcat(x1, collect(product(fill(summands, x2)...))), [], 1:n))
(IterTools.jl is required for product())
Julia 0.7
using Iterators
n = 3
summands = [1, 2, 6, 4]
map(x -> (sum(x), x), reduce((x1, x2) -> vcat(x1, vec(collect(product(fill(summands, x2)...)))), 1:n; init = Vector{Tuple{Int, NTuple{n, Int}}}[]))
(In Julia 0.7, the parameter position of the neutral element changed from 2nd to 3rd argument.)
How does this work?
Let's indent the one-liner (using the Julia 0.6 version, the idea is the same for the Julia 0.7 version):
map(
# Map the possible combinations of `1:n` entries of `summands` to a tuple containing their sum and the summands used.
x -> (sum(x), x),
# Generate all possible combinations of `1:n`summands of `summands`.
reduce(
# Concatenate previously generated combinations with the new ones
(x1, x2) -> vcat(
x1,
vec(
collect(
# Cartesian product of all arguments.
product(
# Use `summands` for `x2` arguments.
fill(
summands,
x2)...)))),
# Specify for what lengths we want to generate combinations.
1:n;
# Neutral element (empty array).
init = Vector{Tuple{Int, NTuple{n, Int}}}[]))
Julia 0.6
This is really just to get a free critique from the experts as to why my method is inferior to theirs!
using Combinatorics, BenchmarkTools
function nsum(a::Vector{Int}, n::Int)::Vector{Tuple{Int, Vector{Int}}}
r = Vector{Tuple{Int, Vector{Int}}}()
s = with_replacement_combinations(a, n)
for i in s
push!(r, (sum(i), i))
end
return sort!(r, by = x -> x[1])
end
#btime nsum([1, 2, 6, 4], 3)
It runs in circa 4.154 μs on my 1.8 GHz processor for n = 3. It produces a sorted array showing the sum (which may appear more than once) and how it is made up (which is unique to each instance of the sum).

SML: Combining Two Lists

I have the following function for combining two lists into one. It's supposedly of type:
# : 'a list * 'a list -> 'a list
fun # (nil, k) = k
| # (x::l, k) = x :: #(l,k);
Let's say we have two lists: [1, 2, 3] and [4, 5, 6]. If I call:
#([1, 2, 3], [4, 5, 6])
1::#([2, 3], [4, 5, 6])
1::2::#([3], [4, 5, 6])
1::2::3::#(nil, [4, 5, 6])
But here we reach the base case and our # call returns the list [4, 5, 6], yielding:
1::2::3::[4, 5, 6]
which is obviously not what I want. Is the function definition correct or am I misunderstanding something?
Yes your function definition is correct.
:: or Cons as it is called in Lisp and other functional programming languages is used for creating lists. It takes a value and a list (which may be empty) and creates a new list with the former prepended to the latter. So for example 42::[17, 23] equals [42, 17, 23].
Cons is right associative which means that your list
1::2::3::[4,5,6]
can be written as
(1::(2::(3::[4,5,6])))
and by successive reductions we get
[1,2,3,4,5,6]

Prolog predicate makelist

Define a Prolog predicate makelist/3 such that makelist(Start, End, List) is true if
List is a list of all integers from the integer Start to the integer End. For example:
makelist(3, 7, [3, 4, 5, 6, 7]) should be true.
Can't understand why my code doesn't work
makelist(H, L, _) :-
L is H+1.
makelist(H, L, List) :-
append([], [H], List), H1 is H+1.
makelist(H1, L, List) :-
append(List, [H1], List1), last(List1, R),
R \= L+1, makelist(N, L, List1), N is H1+1.
You can simplify your code, let's take your predicate and examine what is what you really need to do:
% makelist(X,Y,L)
Since your recursive call is increasing by 1 the first parameter, let's call it X, then your base case would be when X is the same than Y:
makelist(X,X,[X]) .
and your recursive call: it will be when X is smaller than Y, you need to increase X and add the value to the list:
makelist(X,Y,[X|L]) :- X < Y ,
X1 is X + 1 ,
makelist(X1, Y, L).

Resources