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 want to write a function that does this using one line only which means no recursic function and no if condition also no pattern matching
i began by thinking i can cutt the list first only to elements between start and end using :
fun cutt list1 (start,end,interval) = List.drop(List.take(list1 ,end+1),start);
and now i want to use maybe foldl or foldr to only get elements in interval jumps , my problem is how to iterate over the list ?
In theory, after much soul-searching, we can do this, but it's hideously ugly and inefficient and should not be done.
Let's first off drop the elements you want dropped. Your basic recipe works, but you can't use end as that is a reserved word.
fun cutt list1 (start, stop, interval) =
List.drop(List.take(list1, stop + 1), start);
cutt [1,2,3,4,77,8,7] (1, 5, 2) yields [2,3,4,77,8].
Now, how do we drop elements at an interval. We can't use explicit recursion, nor pattern matching or conditions. Well, then we can use List.tabulate to create a list equal to our lists length that has tuples with the list elements and an index.
fun cutt list1 (start, stop, interval) =
List.tabulate(
stop - start + 1,
(fn i =>
(i, List.nth(List.drop(List.take(list1, stop + 1), start), i))));
If we evaluate cutt [1,2,3,4,77,8,7] (1, 5, 2) now we get [(0, 2), (1, 3), (2, 4), (3, 77), (4, 8)].
Now we can filter based on the index to throw out the values we don't want.
fun cutt list1 (start, stop, interval) =
List.filter (fn (i, _) => i mod interval = 0) (List.tabulate(
stop - start + 1,
(fn i =>
(i, List.nth(List.drop(List.take(list1, stop + 1), start), i)))));
cutt [1,2,3,4,77,8,7] (1, 5, 2) yields [(0, 2), (2, 4), (4, 8)].
And we can map out the indexes.
fun cutt list1 (start, stop, interval) =
List.map (fn (_, x) => x) (List.filter (fn (i, _) => i mod interval = 0) (List.tabulate(
stop - start + 1,
(fn i =>
(i, List.nth(List.drop(List.take(list1, stop + 1), start), i))))));
cutt [1,2,3,4,77,8,7] (1, 5, 2) yields [2, 4, 8].
Stunningly ugly and inefficient. I'm not sure why anyone is teaching you to write code this way. Feel free to make it extra ugly by removing the newlines.
fun cutt list1 (start, stop, interval) = List.map (fn (_, x) => x) (List.filter (fn (i, _) => i mod interval = 0) (List.tabulate(stop - start + 1, (fn i => (i, List.nth(List.drop(List.take(list1, stop + 1), start), i))))));
Related
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.
I am learning OCaml and now stuck with a code.
This is a code that makes a list of the accessible nodes from a graph.
type graph = (vertex * vertex) list
and vertex = int
let rec sort lst =
match lst with
[] -> []
| h::t -> insert h (sort t)
and insert n lst =
match lst with
[] -> [n]
| h::t -> if n <= h then n :: lst else h :: insert n t;;
let rec remove lst =
match lst with
| [] -> []
| x::[] -> x::[]
| x::y::tl ->
if x=y then remove (y::tl)
else x::remove (y::tl);;
let rec reach : graph * vertex -> vertex list
= fun (g, v) ->
match g with
|[] -> []
|h::t ->let x = [] in
match h with
|a,b -> if a = v then remove(sort(v::x) # (reach (g, b)))
else
remove(sort(v::reach (t, v)));;
reach([(1,2);(2,3);(3,4);(4,2);(2,5)],4);;
I think my code is meaninglessly complicated due to my lack of coding ability.
Besides, the main problem that I confront now is that I cannot make the recursive function 'reach' to access with original list 'g' as it goes recursive in else condition as it access with list 't'.
trace says
reach <-- ([(1, 2); (2, 3); (3, 4); (4, 2); (2, 5)], 4)
reach <-- ([(2, 3); (3, 4); (4, 2); (2, 5)], 4)
reach <-- ([(3, 4); (4, 2); (2, 5)], 4)
reach <-- ([(4, 2); (2, 5)], 4)
reach <-- ([(4, 2); (2, 5)], 2)
reach <-- ([(2, 5)], 2)
reach <-- ([(2, 5)], 5)
reach <-- ([], 5)
reach --> []
reach --> [5]
reach --> [2; 5]
reach --> [2; 5]
reach --> [4; 2; 5]
reach --> [2; 4; 5]
reach --> [2; 4; 5]
reach --> [2; 4; 5]
- : vertex list = [2; 4; 5]
First, I claimed a new variable with let y = g and change code
|a,b -> if a = v then remove(sort(v::x) # (reach (y, b)))
else
remove(sort(v::reach (t, v)));;
as I believed that the duplicates will be removed by fun 'remove' and inner function will access with list y, not t which lost its head. However, things did
not go as I planned. It still gives me a same result.
To make function access with original list 'g' in else condition what should I do...?
reach <-- ([(1, 2); (2, 3); (3, 4); (4, 2); (2, 5)], 4)
reach <-- ([(2, 3); (3, 4); (4, 2); (2, 5)], 4)
reach <-- ([(3, 4); (4, 2); (2, 5)], 4)
reach <-- ([(4, 2); (2, 5)], 4)
reach <-- ([(1, 2); (2, 3); (3, 4); (4, 2); (2, 5)], 2)
(*I want function goes back to original list as the variable changes like above*)
You can define an auxiliary function:
let reach g v =
let rec aux g' v' = ... (* here you can use g as the auxiliary function defines g' instead *)
in aux g v
Note that I also define the function with two arguments instead of one tuple, it's more idiomatic :)
Also if v' is always the same value the auxiliary function need not redefine it
let reach g v =
let rec aux g' = ...
in aux g v
One more remark. You can du deeper pattern matching, e.g.:
match g with
|[] -> []
|(a,b)::t -> let x = [] in
if a = v
then remove(sort(v::x) # (reach (g, b)))
else remove(sort(v::reach (t, v)));;
No need for a second match.
Finally, you might be aware of the function keyword that creates a function of one argument creating pattern matching:
let f x = match x with
(* same as *)
let f = function
Therefore:
let reach (g:graph) (v:vertex) =
let aux v' = function (*we don't create a g' variable here, we match it straight *)
| [] -> []
| (a,b)::t as g' -> (* you can still define the variable in the matching if you need it *)
let x = [] in
if a = v
then remove(sort(v::x) # (aux b g')) (* or g if that's what you need *)
else remove(sort(v::aux v t))
in aux v g
would do the same as your code
Edit: correctected recusrive calls to reach by recursive calls to aux as it wouldn't work.
Answering directly to your question, here is how to access the original value in a recursive function
let to_matrix outer =
let rec loop = function
| [] -> []
| x :: xs -> [x::outer] # loop xs in
loop outer
This function will iterate over each element of a list and create a list of list, where each element is a list made of the original element prepended to the whole original list, e.g.,
# to_matrix [1;2;3];;
- : int list list =
[[1; 1; 2; 3]; [2; 1; 2; 3]; [3; 1; 2; 3]]
The example might be silly, but I hope it clarifies the technique. Here the idea is that we create an inner function, named loop in our case, which has its own parameter, and we recurse only on this parameter while leaving the original outer list intact.
While it is an idiomatic approach, in general it is not required to create an inner function or an auxiliary function. What is really needed, is an extra parameter, so that at the end of the day we will have two parameters - one which decreases with each iteration and the other which stays invariant, e.g.,
let rec to_matrix outer inner = match inner with
| [] -> []
| x :: xs -> [x::outer] # to_matrix outer xs
the only caveat of this approach is that now we have to pass the list twice, e.g.,
to_matrix [1;2;3] [1;2;3];;
That's why it is idiomatic to hide this function by a nicer interface.
I would like to define a function that takes an integer n and returns an integer n* such that n and n* are in the same set of integers from 1 to n,and the function must be bijective.
I tried the following
fun bij(n) =
let
val ls = zip(upto (1, n), List.rev(upto (1, n)))
val Tw_2 = fn(a, b) => b
in Tw_2(List.last(ls, n-1)) end;
but unfortunately, it returns 1 for all my values of n. I am really stuck here.
Can anyone give me some ideas on how to implement this?
The behavior of bij must look something like
bij(1) = 3
bij(2) = 2
bij(3) = 1
If I understand your question correctly, an easy solution would be:
fun bij(n, i) = n + 1 - i;
Which can be represented by the following table
i | 1 2 3 ... n-2 n-1 n
bij(n, i) | n n-1 n-2 ... 3 2 1
and which works as expected for numbers between 1 and n. Intuitively a (positive) number i is i steps "to the right of 0" and we map this to a number that is i (actually i - 1) steps "to the left of n".
Maybe you wanted to construct the above table explicitly via lists?
fun upto(m, n) = if n < m then [] else m :: upto(m+1, n);
fun table n = ListPair.zip (upto(1, n), List.rev (upto(1, n)));
Example:
> table 5;
val it = [(1, 5), (2, 4), (3, 3), (4, 2), (5, 1)]: (int * int) list
Then to get the i-th pair of a list xs you could use
List.nth (xs, i-1)
Putting all together
fun bij(n, i) =
let
val table = ListPair.zip (upto(1, n), List.rev (upto(1, n)));
fun snd(x, y) = y;
in snd(List.nth (table, i-1)) end;
Which does the same as the initial function, except in a more complicated way.
What is the easiest way to subtract one list from another? Do I need to use ListPair to solve this task? Notic that I need to compare ROWS, not single elements. For instance, there are two lists "L1" and "L2":
L1 =
[(1, 2, 3),
(4, 5, 6)]
L2 =
[(1, 2, 3),
(4, 5, 6),
(3, 2, 3]
I need to get "L3" by applying L3 = L2-L1:
L3 =
[(3, 2, 3)]
Thanks.
As I understand the question, you want to remove the elements in L2 which are also in L1, but only once per occurrence.
A simple solution might involve a helper function to tell you if an element was found in L1, along with the rest of L1 with this element removed.
fun remFirst _ [] rest = (false, rev rest)
| remFirst x (y::ys) rest =
if x = y then
(true, rev rest # ys)
else
remFirst x ys (y :: rest)
Now you can iterate through L2, discarding elements each time remFirst returns true, and then proceeding with the rest of the list.
If instead you want to remove the prefix which L2 has in common with L1, things get a bit simpler.
fun remPref [] _ = []
| remPref xs [] = xs
| remPref (x::xs) (y::ys) = if x = y then remPref xs ys else (x::xs)
UPDATE: The question has now been altered.
If the requirement is now to remove elements from L2 that are in L1, filter is useful.
List.filter (fn x => List.all (fn y => x <> y) L1) L2
Does Haskell have similar syntactic sugar to Python List Slices?
For instance in Python:
x = ['a','b','c','d']
x[1:3]
gives the characters from index 1 to index 2 included (or to index 3 excluded):
['b','c']
I know Haskell has the (!!) function for specific indices, but is there an equivalent "slicing" or list range function?
There's no built-in function to slice a list, but you can easily write one yourself using drop and take:
slice :: Int -> Int -> [a] -> [a]
slice from to xs = take (to - from + 1) (drop from xs)
It should be pointed out that since Haskell lists are singly linked lists (while python lists are arrays), creating sublists like that will be O(to), not O(to - from) like in python (assuming of course that the whole list actually gets evaluated - otherwise Haskell's laziness takes effect).
If you are trying to match Python "lists" (which isn't a list, as others note) then you might want to use the Haskell vector package which does have a built in slice. Also, Vector can be evaluated in parallel, which I think is really cool.
No syntactic sugar. In cases where it's needed, you can just take and drop.
take 2 $ drop 1 $ "abcd" -- gives "bc"
I don't think one is included, but you could write one fairly simply:
slice start end = take (end - start + 1) . drop start
Of course, with the precondition that start and end are in-bounds, and end >= start.
Python slices also support step:
>>> range(10)[::2]
[0, 2, 4, 6, 8]
>>> range(10)[2:8:2]
[2, 4, 6]
So inspired by Dan Burton's dropping every Nth element I implemented a slice with step. It works on infinite lists!
takeStep :: Int -> [a] -> [a]
takeStep _ [] = []
takeStep n (x:xs) = x : takeStep n (drop (n-1) xs)
slice :: Int -> Int -> Int -> [a] -> [a]
slice start stop step = takeStep step . take (stop - start) . drop start
However, Python also supports negative start and stop (it counts from end of list) and negative step (it reverses the list, stop becomes start and vice versa, and steps thru the list).
from pprint import pprint # enter all of this into Python interpreter
pprint([range(10)[ 2: 6], # [2, 3, 4, 5]
range(10)[ 6: 2:-1], # [6, 5, 4, 3]
range(10)[ 6: 2:-2], # [6, 4]
range(10)[-8: 6], # [2, 3, 4, 5]
range(10)[ 2:-4], # [2, 3, 4, 5]
range(10)[-8:-4], # [2, 3, 4, 5]
range(10)[ 6:-8:-1], # [6, 5, 4, 3]
range(10)[-4: 2:-1], # [6, 5, 4, 3]
range(10)[-4:-8:-1]]) # [6, 5, 4, 3]]
How do I implement that in Haskell? I need to reverse the list if the step is negative, start counting start and stop from the end of the list if these are negative, and keep in mind that the resulting list should contain elements with indexes start <= k < stop (with positive step) or start >= k > stop (with negative step).
takeStep :: Int -> [a] -> [a]
takeStep _ [] = []
takeStep n (x:xs)
| n >= 0 = x : takeStep n (drop (n-1) xs)
| otherwise = takeStep (-n) (reverse xs)
slice :: Int -> Int -> Int -> [a] -> [a]
slice a e d xs = z . y . x $ xs -- a:start, e:stop, d:step
where a' = if a >= 0 then a else (length xs + a)
e' = if e >= 0 then e else (length xs + e)
x = if d >= 0 then drop a' else drop e'
y = if d >= 0 then take (e'-a') else take (a'-e'+1)
z = takeStep d
test :: IO () -- slice works exactly in both languages
test = forM_ t (putStrLn . show)
where xs = [0..9]
t = [slice 2 6 1 xs, -- [2, 3, 4, 5]
slice 6 2 (-1) xs, -- [6, 5, 4, 3]
slice 6 2 (-2) xs, -- [6, 4]
slice (-8) 6 1 xs, -- [2, 3, 4, 5]
slice 2 (-4) 1 xs, -- [2, 3, 4, 5]
slice (-8)(-4) 1 xs, -- [2, 3, 4, 5]
slice 6 (-8)(-1) xs, -- [6, 5, 4, 3]
slice (-4) 2 (-1) xs, -- [6, 5, 4, 3]
slice (-4)(-8)(-1) xs] -- [6, 5, 4, 3]
The algorithm still works with infinite lists given positive arguments, but with negative step it returns an empty list (theoretically, it still could return a reversed sublist) and with negative start or stop it enters an infinite loop. So be careful with negative arguments.
I had a similar problem and used a list comprehension:
-- Where lst is an arbitrary list and indc is a list of indices
[lst!!x|x<-[1..]] -- all of lst
[lst!!x|x<-[1,3..]] -- odd-indexed elements of lst
[lst!!x|x<-indc]
Perhaps not as tidy as python's slices, but it does the job. Note that indc can be in any order an need not be contiguous.
As noted, Haskell's use of LINKED lists makes this function O(n) where n is the maximum index accessed as opposed to python's slicing which depends on the number of values accessed.
Disclaimer: I am still new to Haskell and I welcome any corrections.
When I want to emulate a Python range (from m to n) in Haskell, I use a combination of drop & take:
In Python:
print("Hello, World"[2:9]) # prints: "llo, Wo"
In Haskell:
print (drop 2 $ take 9 "Hello, World!") -- prints: "llo, Wo"
-- This is the same:
print (drop 2 (take 9 "Hello, World!")) -- prints: "llo, Wo"
You can, of course, wrap this in a function to make it behave more like Python. For example, if you define the !!! operator to be:
(!!!) array (m, n) = drop m $ take n array
then you will be able to slice it up like:
"Hello, World!" !!! (2, 9) -- evaluates to "llo, Wo"
and use it in another function like this:
print $ "Hello, World!" !!! (2, 9) -- prints: "llo, Wo"
I hope this helps, Jon W.
Another way to do this is with the function splitAt from Data.List -- I find it makes it a little easier to read and understand than using take and drop -- but that's just personal preference:
import Data.List
slice :: Int -> Int -> [a] -> [a]
slice start stop xs = fst $ splitAt (stop - start) (snd $ splitAt start xs)
For example:
Prelude Data.List> slice 0 2 [1, 2, 3, 4, 5, 6]
[1,2]
Prelude Data.List> slice 0 0 [1, 2, 3, 4, 5, 6]
[]
Prelude Data.List> slice 5 2 [1, 2, 3, 4, 5, 6]
[]
Prelude Data.List> slice 1 4 [1, 2, 3, 4, 5, 6]
[2,3,4]
Prelude Data.List> slice 5 7 [1, 2, 3, 4, 5, 6]
[6]
Prelude Data.List> slice 6 10 [1, 2, 3, 4, 5, 6]
[]
This should be equivalent to
let slice' start stop xs = take (stop - start) $ drop start xs
which will certainly be more efficient, but which I find a little more confusing than thinking about the indices where the list is split into front and back halves.
Why not use already existing Data.Vector.slice together with Data.Vector.fromList and Data.Vector.toList (see https://stackoverflow.com/a/8530351/9443841)
import Data.Vector ( fromList, slice, toList )
import Data.Function ( (&) )
vSlice :: Int -> Int -> [a] -> [a]
vSlice start len xs =
xs
& fromList
& slice start len
& toList
I've wrote this code that works for negative numbers as well, like Python's list slicing, except for reversing lists, which I find unrelated to list slicing:
slice :: Int -> Int -> [a] -> [a]
slice 0 x arr
| x < 0 = slice 0 ((length arr)+(x)) arr
| x == (length arr) = arr
| otherwise = slice 0 (x) (init arr)
slice x y arr
| x < 0 = slice ((length arr)+x) y arr
| y < 0 = slice x ((length arr)+y) arr
| otherwise = slice (x-1) (y-1) (tail arr)
main = do
print(slice (-3) (-1) [3, 4, 29, 4, 6]) -- [29,4]
print(slice (2) (-1) [35, 345, 23, 24, 69, 2, 34, 523]) -- [23,24,69,32,34]
print(slice 2 5 [34, 5, 5, 3, 43, 4, 23] ) -- [5,3,43]
Obviously my foldl version loses against the take-drop approach, but maybe someone sees a way to improve it?
slice from to = reverse.snd.foldl build ((from, to + 1), []) where
build res#((_, 0), _) _ = res
build ((0, to), xs) x = ((0, to - 1), x:xs)
build ((from, to), xs) _ = ((from - 1, to - 1), xs)
sublist start length = take length . snd . splitAt start
slice start end = snd .splitAt start . take end