how to step over a list in sml? - sml

i have a list , and a tuple (start,end,interval)
i am trying to iterate over the list and return the elements that are in list from start to end with interval steps .
for example:
cutt [1,2,3,4,77,8,7] (1,5,2);
val it = [2,4,8] : int list;
the problem is that i can't use recursion ,
i was thinking to use foldl but the problem is how to skip over elements i don't want to use list.nth?
i would appriciate little hints ! thank you

It sounds like you need two functions. The first step is to retrieve a slice of your list between indices. This sounds suspiciously related to a solution posted very recently.
fun slice'(lst, start, stop) =
let
val (_, lst') = foldl
(fn (v, (idx, acc)) =>
if idx >= start andalso idx <= stop then
(idx + 1, v::acc)
else
(idx + 1, acc))
(0, [])
lst
in
List.rev(lst')
end;
If we try slice'([1,2,3,4,77,8,7], 1, 5) the result is [2, 3, 4, 77, 8].
To get to [2, 4, 8] we just need to drop every second element. This can be defined in terms of simple recursion as follows.
fun dropn(n, lst) =
let
fun dropn'(_, _, []) = []
| dropn'(n, c, x::xs) =
if c = n then x :: dropn'(n, 1, xs)
else dropn'(n, c + 1, xs)
in
dropn'(n, n, lst)
end
But apparently you can't use recursion, so let's use a fold.
fun drop(n, lst) =
List.foldl
(fn (x, (acc, i)) => if i = n then (x :: acc, 1) else (acc, i + 1))
([], n)
lst
Now if we try drop(2, [1,2,3,4,77,8,7]) we get ([7, 77, 3, 1], 1) which does contain the list we want, but also the counter, and the list is backwards because of the way we've built it up. Let's consider how that accumulator works as we step through this simple example.
Initial state:
lst: [1,2,3,4,77,8,7]
acc: []; i: 2
First iteration:
x: 1
acc: [1]; i: 2
Second Iteration:
x: 2
acc: 1 :: []; i: 1
Third iteration:
x: 3
acc: 3 :: 1 :: []; i: 2
Fourth iteration:
x: 4
acc: 3 :: 1 :: []; i: 1
Fifth iteration:
x: 77
acc: 77 :: 3 :: 1 :: []; i: 2
Sixth iteration:
x: 8
acc: 77 :: 3 :: 1 :: []; i: 1
Seventh iteration:
x: 7
acc: 7 :: 77 :: 3 :: 1 :: []; i: 2
Final step:
remaining list: []
acc: 7 :: 77 :: 3 :: 1 :: [] or [7, 77, 3, 1]; i: 1
Easy enough, though, to extract and reverse that list.
fun drop(n, lst) =
let
val (lst', _) = List.foldl
(fn (x, (acc, i)) => if i = n then (x :: acc, 1) else (acc, i + 1))
([], n)
lst
in
List.rev lst'
end
So now drop(2, [1,2,3,4,77,8,7]) evaluates to [1, 3, 77, 7].
Between these two answers, it should be very straightforward to put together a solution to your problem, and hopefully to see why it works.

One solution to correctly skip elements you don't want in your
resulting list, is to add a parameter to your function used to fold
your list. The meaning of this parameter is basically "how much items
you still want to skip".
As an example, below is a solution that uses List.foldl. The
function used to fold items of the list takes two parameters: the
first one is the one discussed above and the second one is the
resulting list being constructed.
fun cutt lst (idx_start, idx_end, interval) = let
val (_, result) =
List.foldl
(fn (x, (0, result)) =>
(* no more item to skip => add x to the resulting list *)
(interval - 1, x :: result)
| (_, (n, result)) =>
(* there are still items to skip *)
(n - 1, result))
(idx_start, []) (* initially we skip idx_start items *)
lst
in
(* items are prepended in the resulting list in reverse order =>
reverse the list *)
List.rev result
end;
val x = cutt [1, 2, 3, 4, 77, 8, 7] (1, 5, 2);
Of course the function needs to be adapted as it does not take care
of the idx_end parameter.

Related

How to split a list int groups under certain conditions?

I want to split a list into groups with n elements. For example:
n = 2
[1, 2, 3, 4, 5, 6] ->[[1, 2], [3, 4], [5,6]]
n = 3
[1, 2, 3, 4, 5, 6] -> [[1, 2, 3] [4, 5, 6]]
I tried to implement a function, which returns n if n is 0 or greater than the length of the list and the fitted list if n is less than the length of the list.
split :: Int -> [a] -> Either Int [[a]]
split n [a]
|n <= lenght [a] = Right n (take n [a]) : (split n (drop n [a]))
|n == 0 = Left n
|otherwise = Left n
However, I get a "variable not in scope" error. I've already tried around, but I'm stuck.
Did I make a mistake with the data types?
You have a typo with lenght vs. length, but if we change that there are still errors.
If we look at Right n (take n [a]) we can see that Right and Left only accept a single argument.
Your pattern split n [a] also only matches a list with a single element.
Let's break this down into smaller pieces. Creating a function that splits a list is straightforward.
split' n [] = []
split' n lst = take n lst : (split' n $ drop n lst)
Prelude> split' 3 [1,2,3,4,5,6]
[[1,2,3],[4,5,6]]
Now it's straightforward to make this local to split to incorporate the checks you specified and return the desired Either type.
split :: Int -> [a] -> Either Int [[a]]
split n [] = Left n
split n lst
| n == 0 || n > length lst = Left n
| otherwise = Right lst'
where
split' n [] = []
split' n lst = take n lst : (split' n $ drop n lst)
lst' = split' n lst

How to iterate through a list, element by element

I have just started learning Haskell and I am trying to write some basic functions in order to get a better understanding of this language.
I want to write a function which takes a list and an Int (N) as argument and returns the element at index N in the list, without using the !! operator or any built-in function.
Here is what I tried :
myHead :: [a] -> a
myHead (x:_) = x
myHead [] = error "head: empty list"
myNth :: [a] -> Int -> a
myNth x i = if i < 0
then error "nth: index can't be negative"
else myNthIterator x i 0
myNthIterator :: [a] -> Int -> Int -> a
myNthIterator [] i n = error "nth: bad index"
myNthIterator (_:x) i n = if i == n
then myHead x
else myNthIterator x i ( n + 1 )
It works but it's shifted to the right. For example myNth [1, 2, 3, 4] 2 would give 4 and not 3.
From what I understand, (_:x) removes the first element of the list and I don't see how to iterate through the list element by element.
Could someone put me on the trail? I find it difficult to find resources for beginners in this language.
We can use Maybe to model whether the index was valid.
nth :: Int -> [a] -> Maybe a
nth 0 (x : _) = Just x
nth n (x : xs) = nth (n - 1) xs
nth _ [] = Nothing
We can pattern match on the index to get our base case, and the list to get the first element and tail.
What you're doing there with (_:x) is called "pattern matching" in case you didn't know. The general pattern for iterating through a list would be (x : xs) where x is head element of the list being matched and xs is the rest of the list. If you use _ you don't remove anything it is still matched to _ which is the convention for saying "I won't use this".
With that you can make a function like this:
myNth :: [a] -> Int -> a
myNth [] _ = error "out of range"
myNth (x : xs) 0 = x
myNth (_ : xs) n = myNth xs (n - 1)
Whenever myNth is called it will go top to bottom over those definitions trying to match the patterns to the input. So when you call myNth [10,11] 1 it won't match the first clause because [10,11] doesn't match an empty list, it won't match the second either because 1 is not 0 and so it will match the third case where it will match the [10,11] on (10 : [11]), therefore _ is 10 and xs is [11] and 1 will be matched as n. Then it calls itself recursively, as myNth [11] 0. Now that will match the second case and it will return x from the match of [11] on (11 : [])
Like 414owen said you can use the Maybe a type to avoid using error.
P.S.: I don't know how beginner you are but I assume you know of the : operator, it prepends an element to a list... If you go more in depth (afaik) every list is actually stored as a sequence of a:(b:(c:(d:(e:[])))) which is equivalent to [a,b,c,d,e] which is equivalent to a:[b,c,d,e] etc.
It works but it's shifted to the right. For example myNth [1, 2, 3, 4] 2 would give 4 and not 3.
myNthIterator (_:x) i n = if i == n
then myHead x
else myNthIterator x i ( n + 1 )
Let us look at myNthIterator [1..4] 1 1
myNthIterator [1..4] 1 1 -- replace [a, b] with (a: (b : []))
== myNthIterator (1 : [2, 3, 4]) 1 1
-- matching with `myNthIterator (_:x) i n` will result in
-- 1 ~ _
-- x ~ [2, 3, 4]
-- i ~ 1
-- n ~ 1
== if 1 == 1 then myHead [2, 3, 4] else myNthIterator [2, 3, 4] 1 (1 + 1)
== myHead [2, 3, 4]
== 2
So (_:x) matching against (1 : [2, 3, 4]) is suspicious. A first step in fixing it is to replace (_:x) by (x:xs).
myNthIterator (x:xs) i n = ...
In our example this would mean x == 1 and xs == [2, 3, 4].

Multiple lists haskell

How do you find nth elements in the matrix at a given row and column position? For example, if you have
type Matrice a = [[a]]
example :: Matrice Int
example = [ [3, 5],
[2, 1],
[0, 4],
[6, 8] ]
Prelude > example 0 1
5
Prelude > example 2 0
0
Prelude > example 1 1
2
I know how to work out with only given list such as
nth :: Int -> [a] -> Maybe a
nth _ [] = Nothing
nth 1 (x : _) = Just x
nth n (_ : xs) = nth (n - 1) xs
But my question is, how do you access nth element in a matrix as in the given example
Just handle each list individually, and !! will work:
Prelude> example
[[3,5],[2,1],[0,4],[6,8]]
Prelude> :t example
example :: Matrice Int
Prelude> example !! 0 !! 1
5
But lists are probably not the right data structure for this, because indexing is O(n). Depending on your task, Data.Vector or Data.Array may be better suited. See also Haskell: Lists, Arrays, Vectors, Sequences.
!! can be used for accessing an element by index, but be careful, since it's raising an exception, if the index is too large.
example !! 2 !! 0
And you've already written a function for accessing nth element of a list, just apply it twice:
nth :: Int -> Int -> [[a]] -> Maybe a
nth k n matrix = nth' n =<< nth' k matrix
where
nth' _ [] = Nothing
nth' 0 (x: _) = Just x
nth' n (_ : xs) = nth' (n - 1) xs
Or using your created Matrice type:
nth :: Matrice a -> Int -> Int -> Maybe a
nth matrix k n = nth' n =<< nth' k matrix
where
nth' _ [] = Nothing
nth' 0 (x: _) = Just x
nth' n (_ : xs) = nth' (n - 1) xs

How to add elements to a list every nth round?

I need to
Write a function separate of type int * 'a * 'a list -> 'a lst such that
separate (k, x, l) returns the list that inserts element x after each k elements of list l (counting from
the end of the list). For example, separate (1, 0, [1,2,3,4]) should return [1,0,2,0,3,0,4] and
separate (3, 0, [1,2,3,4]) should return [1,0,2,3,4].
So far, this is what I have, but it is causing an error. Can anyone help me?
fun separate (k: int, x: 'a, l: 'a list) : 'a list =
let val count:int = k
in foldr(
(fn (h, t) =>
if count = 0
then count := 1 in
x::h::t
else count = count + 1 : int
h::t
)
Actually the logic is quite right, but it should be implemented by passing changed state into another iteration of foldr due to immutability:
fun separate (k: int, x: 'a, l: 'a list) : 'a list =
#2 (foldr (fn (h, (count, t)) =>
if count = 0
then (k - 1, h::x::t)
else (count - 1, h::t)
) (k, []) l);
Thus, instead of initiating count as a variable, we initiate foldr with tuple (k, []) (where k is the initial value of count and [] is the resulting list) and then decrease the count every step of the iteration.

SML - Find occurences in lists to form ordered pairs

I'm trying to write a function in SML that takes in a list of ints and will output a list of ordered pairs of ints. The ordered pairs first int is the int that occurred in the input list and the second int in the ordered pair is the number of times it occurred in the input list. Also the list returned should be in ascending order according to the first int in the ordered pairs.
For example input list [1, 1, 1, 2, 3, 3, 5] would output as [(1,3), (2, 1), (3, 2), (5, 1)].
So far I have a function that uses foldl
UPDATED the code since original post.
fun turnIntoPairs l = foldl (fn (e, a) => if List.exists (fn (x, _) => x = e) a then x + 1 else a # [(e, 1)]) [] l;
I'm having trouble updating the list where I find the ordered pair that is already in the list - I want to add one to the second int in the ordered pair that was found while it's still in the list.
Any help would be greatly appreciated!
C:\Program Files (x86)\SMLNJ\\bin\.run\run.x86-win32.exe: Fatal error -- Uncaught exception Error with 0
raised at ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
[autoloading done]
C:\Users\Localadmin\Desktop\CS 671\Program 3\commonFactors.sml:1.87 Error: unbound variable or constructor: x
C:\Users\Localadmin\Desktop\CS 671\Program 3\commonFactors.sml:1.44-1.110 Error: types of if branches do not agree [literal]
then branch: int
else branch: (''Z * int) list
in expression:
if (List.exists (fn <pat> => <exp>)) a
then <errorvar> + 1
else a # (e,1) :: nil
[Finished in 0.5s with exit code 1]
Not really sure how to fix your current program, but you can solve this problem by splitting it in two: grouping equal elements and then ordering the list.
(* Groups successive equal elements into a tuples (value, count) *)
fun group (l as (x :: _)) =
let val (firstGroup, rest) = List.partition (fn y => x = y) l
in
(x, List.length firstGroup) :: (group rest)
end
| group [] = []
(* Now that we have our elements grouped, what's left is to order
them as required. *)
fun turnIntoPairs xs =
ListMergeSort.sort (fn ((x, _), (y, _)) => x >= y) (group xs)
Let's just look at the function you're passing to foldl:
(fn (e, a) => if List.exists (fn (x, _) => x = e) a then x + 1 else a # [(e, 1)])
The first problem (which the type-checker is complaining about) is that your if expression returns either x + 1, or a # [(e, 1)], which seems problematic on account of the former being a value of type int and the latter being of type (int * int) list.
Let's rewrite your code using some helper functions that I won't define and see if it gets clearer:
(fn (e, a) => if List.exists (fn (x, _) => x = e) a then increment a e else a # [(e, 1)])
Where increment has the type (int * int) list -> int -> (int * int) list.
Can you implement increment?
Like Gian, I would prefer to divide this into two functions: One that folds and one helper function that inserts. Incidentally, the insert function would take an element and an existing (int * int) list just as the accumulator function that fold accepts these two arguments.
Normally I would write an insert function curried (i.e. insert x xs) but if I write it uncurried (i.e. insert (x, xs)), I can pass it directly to foldl:
fun insert (x, []) = [(x,1)]
| insert (x, ((y,c)::xs)) =
if x = y then (y,c+1)::xs else (y,c)::insert (x, xs)
fun turnIntoPairs xs = foldl insert [] xs