SML: Look and Say Function - functional-programming

I'm having trouble with writing the look and say function recursively. It's supposed to take a list of integers and evaluate to a list of integers that "reads as spoken." For instance,
look_and_say([1, 2, 2]) = "one one two twos" = [1, 1, 2, 2]
and
look_and_say([2, 2, 2]) = "three twos" = [3, 2]
I'm having some difficulty figuring out how to add elements to the list (and keep track of that list) throughout my recursive calls.
Here's an auxiliary function I've written that should be useful:
fun helper(current : int, count : int, remainingList : int list) : int list =
if (current = hd remainingList) then
helper(current, count + 1, tl remainingList)
else
(* add count number of current to list *)
helper(hd remainingList, 1, tl remainingList);
And here's a rough outline for my main function:
fun look_and_say(x::y : int list) : int list =
if x = nil then
(* print *)
else
helper(x, 1, y);
Thoughts?

You seem to have the right idea, although it doesn't look as if your helper will ever terminate. Here's a way of implementing it without a helper.
fun look_and_say [] = []
| look_and_say (x::xs) =
case look_and_say xs of
[] => [1,x]
| a::b::L => if x=b then (a+1)::b::L
else 1::x::a::b::L
And here's a way of implementing it with your helper.
fun helper(current, count, remainingList) =
if remainingList = [] then
[count, current]
else if current = hd remainingList then
helper(current, count + 1, tl remainingList)
else count::current::look_and_say(remainingList)
and look_and_say [] = []
| look_and_say (x::y) = helper(x, 1, y)

Related

Create a repeat function in SML

I am working on creating a function called repeat that takes two int lists lst1 and lst2. Assume that lst2 only has nonnegative integers, repeats the integers in the first list lst1 according to the numbers indicated by the second list lst2. If both lists are empty, return an empty list. You may need a local function.
Example:
repeat ([1,2,3], [4,0,3]) -> [1,1,1,1,3,3,3]
I am having a little trouble with getting started with this function. What should I put after the xs?
fun repeat(lst1, lst2) =
case lst1 of
[] => []
| x::xs' => [] (* what should I put here *)
Like any recursion problem, what's your base case? I'd say in this case it's both lists are empty and it gives you an empty list.
fun repeat([], []) = []
What if one is empty but the other isn't? That's a failure. Let's define an exception we can throw if this happens.
exception MismatchedArguments;
fun repeat([], []) = []
| repeat([], _) = raise MismatchedArguments
| repeat(_, []) = raise MismatchedArguments
Now the real question is what we do the rest of the time. Fortunately, SML makes it easy to pattern match both lists and extract their first elements.
exception MismatchedArguments;
fun repeat([], []) = []
| repeat([], _) = raise MismatchedArguments
| repeat(_, []) = raise MismatchedArguments
| repeat(x::xs, y::ys) = ...
At this point, we need a recursive function to repeat an element of the list a certain number of times. As with the overall function, here we see the two hallmarks of recursion: at least one base "exit" condition, and an update step where we converge toward the base condition by updating n to n - 1.
exception MismatchedArguments;
fun repeat([], []) = []
| repeat([], _) = raise MismatchedArguments
| repeat(_, []) = raise MismatchedArguments
| repeat(x::xs, y::ys) =
let
fun repeat'(_, 0) = []
| repeat'(x, n) = x :: repeat'(x, n - 1)
in
...
end
Now, we just need to put it all together, by feeding x and y to repeat' and then concatenating that with the result of calling repeat again with xs and ys. By doing this, we converge down toward the base case of repeat([], []) or we may converge toward a mismatched scenario where a MismatchedArguments exception is raised.
exception MismatchedArguments;
fun repeat([], []) = []
| repeat([], _) = raise MismatchedArguments
| repeat(_, []) = raise MismatchedArguments
| repeat(x::xs, y::ys) =
let
fun repeat'(_, 0) = []
| repeat'(x, n) = x :: repeat'(x, n - 1)
in
repeat'(x, y) # repeat(xs, ys)
end
Now repeat([1, 2, 3], [4, 0, 3]) will yield [1, 1, 1, 1, 3, 3, 3].

filter max of N

Could it be possible to write in FFL a version of filter that stops filtering after the first negative match, i.e. the remaining items are assumed to be positive matches? more generally, a filter.
Example:
removeMaxOf1([1,2,3,4], value>=2)
Expected Result:
[1,3,4]
This seems like something very difficult to write in a pure functional style. Maybe recursion or let could acheive it?
Note: the whole motivation for this question was hypothesizing about micro-optimizations. so performance is very relevant. I am also looking for something that is generally applicable to any data type, not just int.
I have recently added find_index to the engine which allows this to be done easily:
if(n = -1, [], list[:n] + list[n+1:])
where n = find_index(list, value<2)
where list = [1,2,3,4]
find_index will return the index of the first match, or -1 if no match is found. There is also find_index_or_die which returns the index of the first match, asserting if none is found for when you're absolutely certain there is an instance in the list.
You could also implement something like this using recursion:
def filterMaxOf1(list ls, function(list)->bool pred, list result=[]) ->list
base ls = []: result
base not pred(ls[0]): result + ls[1:]
recursive: filterMaxOf1(ls[1:], pred, result + [ls[0]])
Of course recursion can! :D
filterMaxOf1(input, target)
where filterMaxOf1 = def
([int] l, function f) -> [int]
if(size(l) = 0,
[],
if(not f(l[0]),
l[1:],
flatten([
l[0],
recurse(l[1:], f)
])
)
)
where input = [
1, 2, 3, 4, ]
where target = def
(int i) -> bool
i < 2
Some checks:
--> filterOfMax1([1, ]) where filterOfMax1 = [...]
[1]
--> filterOfMax1([2, ]) where filterOfMax1 = [...]
[]
--> filterOfMax1([1, 2, ]) where filterOfMax1 = [...]
[1]
--> filterOfMax1([1, 2, 3, 4, ]) where filterOfMax1 = [...]
[1, 3, 4]
This flavor loses some strong type safety, but is nearer to tail recursion:
filterMaxOf1(input, target)
where filterMaxOf1 = def
([int] l, function f) -> [int]
flatten(filterMaxOf1i(l, f))
where filterMaxOf1i = def
([int] l, function f) -> [any]
if(size(l) = 0,
[],
if(not f(l[0]),
l[1:],
[
l[0],
recurse(l[1:], f)
]
)
)
where input = [
1, 2, 3, 4, ]
where target = def
(int i) -> bool
i < 2

Return the index for a list from a list in Erlang

I've been practicing using recursion to define the index in Erlang. Here I need to implement a function to return the index for a list from a list.
eg.
([2, 4, 4], [1, 1, 2, 4, 4, 3, 4 ]) ----> 2
([1, 3], [5, 2, 2, 3, 1, 3, 5]) ----> 4
([1], [3, 2, a, {1, 1}, 1] ----> 4
Here is my code:
-module(project).
-export([index/2]).
index([X|XS],[_]) -> index([X|XS],[_],1).
index(_,[],_) -> [];
index([X|XS],[X|_], ACC) -> ACC;
index([X|XS],[_|rest],ACC) ->index([X|XS],rest,ACC+1).
I modified and coded logically but it still can not being compiled. I hope someone who can help me with it. Thanks!
Just for fun, here is an implementation that is not written a very clean way, but illustrates the techniques I think you are looking for. Note there are two basic states: "checking" and "matching".
-module(sublistmatch).
-export([check/2]).
check(Segment, List) ->
SegLen = length(Segment),
ListLen = length(List),
Index = 1,
check(Segment, List, SegLen, ListLen, Index).
check(S, S, _, _, I) ->
{ok, I};
check(_, _, SL, LL, _) when SL >= LL ->
nomatch;
check(S = [H|Ss], [H|Ls], SL, LL, I) ->
case matches(Ss, Ls) of
true -> {ok, I};
false -> check(S, Ls, SL, LL - 1, I + 1)
end;
check(S, [_|L], SL, LL, I) ->
check(S, L, SL, LL - 1, I + 1).
matches([H|S], [H|L]) -> matches(S, L);
matches([], _) -> true;
matches(_, _) -> false.
Note that this depends on knowing the lengths of both the segment you are checking for, and the current length of the remaining list to check. Consider why this is necessary. Also consider how using the utility function matches/2 gives us a natural place to explore whether an option matches, and backtracks if it does not.
In real programs you would use the standard library functions such as lists:prefix/2, lists:suffix/2, or sets:is_subset/2, or maybe some key or member operation over a gb_tree, dict, map or array depending on the situation.
To Compile the code you have to change it to:
-module(project).
-export([index/2]).
%%index([X|XS],[_]) -> index([X|XS],[_],1).
index([X|XS],List) -> index([X|XS],List,1).
%% you shuld not pass '_' as parameter it's will be marked as unbound
index(_,[],_) -> [];
index([X|XS],[X|_], ACC) -> ACC;
%%index([X|XS],[_|rest],ACC) ->index([X|XS],rest,ACC+1).
index([X|XS],[_|Rest],ACC) ->index([X|XS],Rest,ACC+1).
%% rest is an atom, it's not the case you need to use here.
%%Variables should start with upper case letter.
This code will compiled but wrong results as some cases.

Remove duplicates from a list in SML

I just started learning functional programming in SML and I want to know how I can combine the following two functions into a single function. The function isolate deletes the duplicates of a list of any type ('a) using the helper function 'removes'.
fun isolate [] = []
| isolate (l as x::xs) = x::isolate(remove(x,xs))
fun remove (x,[]) = []
| remove (x,l as y::ys) = if x = y then remove(x,ys) else y::remove(x,ys)
So, for the purpose of better understanding the constructs in the SML, how would you include the function remove within isolate? This may seem trivial, but I have thinking about it and can't figure it out. Thank you for your help!
One method would be to just define remove inside isolate.
fun isolate [] = []
| isolate (l as x::xs) =
let fun remove (x,[]) = []
| remove (x,l as y::ys) = if x = y
then remove(x,ys)
else y::remove(x,ys)
in
x::isolate(remove(x,xs))
end
Alternately, to make deduplication one function, though all this really does is use the library function List.filter to do the same thing that remove does.
fun isolate [] = []
| isolate (x::xs) = x::isolate(List.filter (fn y => y <> x) xs)
My idea: define a nested function to check if there are duplicated elements in the list:
fun set(nums:int list)=
let fun duplicate(x:int, l:int list)=
if null l
then false
else hd l=x orelse duplicate(x,tl l)
in
if null nums
then []
else
let val s=set(tl nums)
in if duplicate(hd nums,s)
then s
else hd nums::s
end
end
But it will give a list that only remains the last one for every duplicated elements.
I want to propound the following solutions of this problem:
fun remove_duplicates(xs: int list) =
let
fun check(xs: int list, item: int) =
if null xs
then false
else if hd xs = item
then true
else check (tl xs, item)
fun go_through_list(xs: int list) =
if null xs
then []
else if check(tl xs, hd xs)
then go_through_list(tl xs)
else hd xs :: go_through_list(tl xs)
in
go_through_list(xs)
end
It's more lines of code than in the solution propounded by #qaphla
My idea is to first sort the list, then recursively return a new list without duplicates:
fun remove_duplicates(l: int list) =
if null(l)
then []
else if null(tl l)
then l
else
let
fun compare(x: int, y: int) = x > y
fun sort(l: int list) = ListMergeSort.sort(compare) l
val l_sorted = sort(l)
in
if (hd l_sorted) = (hd (tl l_sorted))
then remove_duplicates(tl l_sorted)
else (hd l_sorted)::remove_duplicates(tl l_sorted)
end

Standard ML - Return the index of the occurrences of a given value in a list

I'm trying to figure out how to return a list of the indexes of occurrences of a specific value in another list.
i.e.
indexes(1, [1,2,1,1,2,2,1]);
val it = [1,3,4,7] int list
I'm trying to figure out how lists work and trying to get better at recursion so I don't want to use List.nth (or any library functions) and I don't want to move into pattern matching quiet yet.
This is what I have so far
fun index(x, L) =
if null L then 0
else if x=hd(L) then
1
else
1 + index(x,tl L);
fun inde(x, L) =
if null L then []
else if x=hd(L) then
index(x, tl L) :: inde(x, tl L)
else
inde(x, tl L);
index(4, [4,2,1,3,1,1]);
inde(1,[1,2,1,1,2,2,1]);
This gives me something like [2, 1, 3, 0]. I guess I'm just having a hard time incrementing things properly to get the index. The index function itself works correctly though.
Instead you could also make two passes over the list: first add an index to each element in the list, and second grap the index of the right elements:
fun addIndex (xs, i) =
if null xs then []
else (hd xs, i) :: addIndex(tl xs, i+1)
fun fst (x,y) = x
fun snd (x,y) = y
fun indexi(n, xs) =
if fst(hd xs) = n then ... :: indexi(n, tl xs)
else indexi(n, tl xs)
(I left out part of indexi for the exercise.)
Where addIndex([10,20,30],0) gives you [(10,0),(20,1),(30,2)]. Now you can use addIndex and indexi to implement your original index function:
fun index(n, xs) = indexi(n, addIndex(xs, 0))
When you get that to work, you can try to merge addIndex and indexi into one function that does both.
However, you really want to write this with pattern matching, see for instance addIndex written using patterns:
fun addIndex ([], _) = []
| addIndex (x::xs, i) = (x,i) :: addIndex(xs, i+1)
If you do index(1,[2]), it gives 1, which is not correct. When the list is empty, it gives you zero. In a function like this, you'd probably want to use SOME/NONE feature.

Resources