range function not passing all tests - sml

I am writing a range function that is supposed to return a list of integers in a specified range.
range(2,12,3) should return the list [2,5,8,11]
range(10,2,~1) should return the list [10,9,8,7,6,5,4,3]
for range(2,12,3) this works:
fun range(start, stop, step) =
if start > stop then nil
else start::range(start+step, stop, step);
for range(10,2,~1) this works:
fun range(start,stop,step) =
if start <= stop then nil
else start::range(start+step, stop, step);
However, I need one function to handle both cases. I have tried using orelse and else if as follows but the function returns an empty list.
orelse:
fun range(start, stop, step) =
if start > stop orelse start <= stop then nil
else start::range(start+step, stop, step);
else if:
fun range(start,stop,step) =
if start > stop then nil
else if start <= stop then nil
else start::range(start+step,stop,step);
Both methods return
val it = [] : int list
I think it has something to do with using nil in the base case but I don't know why.
What needs to be changed so that the function returns the expected output for both tests?
Thank you

Instead of comparing stop to start, consider the sign of stop - start and the sign of step.
If you think about that for a while, you will see that the signs are the same as long as you are inside the range.
fun sgn x = if x < 0
then ~1
else if x > 0
then 1
else 0;
fun range (start, stop, step) = if sgn(stop-start) = sgn(step)
then start :: range (start + step, stop, step)
else []

Problem:
In both of your solutions the condition will be always true, because you're checking if start is either less than, greater than, or equal to stop, and this is matematically true for any pair of numbers.
Solution:
You must check the current situation according to the sign of step.
If step is zero, do not check anything else, as creating a range of step 0 is not possible. Throwing an exception would be a good option.
If step is positive, check that start is lower than stop. If not, stop.
If step is negative, check that start is higher than stop. If not, stop.
Full code:
fun range(start,stop,step) =
if start = stop orelse
(start < stop andalso step < 0) orelse
(start > stop andalso step > 0) then nil
else start::range(start+step, stop, step);

Related

SML : drop the first n elements of a list

So, I know there is a build in function to drop elements but it works if you wish to drop the last n elements.
I want to drop the first n elements.
An example:
> drop([1,2,3,4], 2)
> [1,2]
I've wrote this piece of code:
exception Subscription;
val counter = 0;
fun drop (l,0) = l
| drop([], n) = raise Subscription
| drop(h::x, n) = if n<0 then raise Subscription
else if(counter <n) then
let
val counter = counter +1
in
(h:: drop(x,n))
end
else nil
;
drop([1,2,3,4],2);
it won't be compiled. The error message I get is :
uncaught exception Subscription
raised at: dropper.ml:6.23-6.35
Any thoughts and help appreciated.
You are misunderstanding the meaning of a val declaration. It does
not declare a variable of which you can change the content afterwards
as in imperative programming languages. Basically:
as mentioned by #kopec, your test if(counter < n) is pointless
since it reduces to if(0 < n) as counter as been set to 0, i.e.,
the val counter = counter + 1 placed after has no effect on that
test
the declaration val counter = counter + 1 is useless. It
basically means "now counters is set to counters + 1 (so 1) only in
the in ... end block following" where it's not used at all.
Your code is thus equivalent to:
exception Subscription;
val counter = 0;
fun drop (l,0) = l
| drop([], n) = raise Subscription
| drop(h::x, n) = if n<0 then raise Subscription
else if(0 <n) then (h:: drop(x,n))
else nil
;
drop([1,2,3,4],2);
Hence, it's clear that with your example list you'll keep performing
recursive call ((h:: drop(x,n))) until reaching the third clause of
your function that raises the exception.
In this kind of situation, a proper way to solve your problem is to
use the arguments of your recursive function:
exception Subscription;
fun drop(l, 0) = l
| drop([], n) = raise Subscription
| drop(h :: x, n) =
if n < 0 then raise Subscription
else (h :: drop(x, n - 1))
;
drop([1,2,3,4],2);
Hence, if n is greater or equal to the length of the input list, you
will eventually fall in the first clause of the function.
(Happy to see that you are getting familiar with pattern matching :))

Problem with larg index when testing my code

I am trying to learn Haskell, I want to write a recursive function and do not use any library functions. The function
nth ::Integer -> [a ] -> Maybe a
takes an index n and a list of elements and returns the n-th element of the list (if the index is valid) or Nothing if
the index is invalid.
My code:
nth :: Integer -> [a] -> Maybe a
nth a [] = Nothing
nth a (x:xs) |a == 1 = Just x
|fromIntegral (length xs) < a = Nothing
|a==0 = Nothing
| otherwise = nth (a-1) xs
I want to do this test to my code:
spec = do
describe "nth" $ do
it "for valid indexes it behaves like (!!)" $
property $ \n xs -> n < 0 || (fromInteger n) >= length (xs::[Integer]) || Lists.nth n xs == Just (xs!!(fromInteger n))
it "for negative indexes it returns Nothing" $
property $ \n xs -> n >= 0 || Lists.nth n (xs::[Integer]) == Nothing
it "for too large indexes it returns Nothing" $
property $ \n xs -> (fromInteger n) < length xs || Lists.nth n (xs::[Integer]) == Nothing
but every time I am doing the test I'm getting an error
for valid indexes it behaves like (!!) FAILED [1]
for negative indexes it returns Nothing
+++ OK, passed 100 tests.
for too large indexes it returns Nothing FAILED [2]
1) Lists.nth for valid indexes it behaves like (!!)
Falsified (after 5 tests and 5 shrinks):
0
[0]
To rerun use: --match "/Lists/nth/for valid indexes it behaves like (!!)/"
./ListsSpec.hs:23:9:
2) Lists.nth for too large indexes it returns Nothing
Falsified (after 38 tests):
1
[0]
There are some problems here with your function. The reason why the first case (behaving like (!!)) fails, is because (!!) :: Int -> [a] -> a uses a zero-based index, whereas your function seems to work with a one-based index. That means that you will thus need to decrement the index you give to the function.
Furthermore in your function you make a a comparison between n and fromIntegral (length xs). Since xs is the tail of the list, the check is not correct since it will, in certain circumstances, never consider the last element. Indeed:
Prelude> nth 2 [0, 2]
Nothing
Furthermore it is typically not a good idea to use length in each iteration. length runs in O(n), that means that your algorithm now runs in O(n2), so as the list grows, this easily will start taking considerable time.
A shorter and more elegant way to fix this is probably:
nth :: Integral i => i -> [a] -> Maybe a
nth 1 (x:_) = Just x
nth i (_:xs) | i < 1 = Nothing
| otherwise = nth (i-1) xs
nth _ [] = Nothing
Here we thus have four cases: in case the index is 1 and the list is non-empty, we return the head of the list, wrapped in a Just. If the index is not one, and it is less than one, then the index is too small, and hence we return Nothing (this case is strictly speaking not necessary). If i is greater than one, then we call nth (i-1) xs. Finally if we have reached the end of the list (or the list was empty in the first place), we return Nothing as well).
Now in order to test this, we thus need to rewrite these three cases:
describe "nth" $ do
it "for valid indexes it behaves like (!!)" $
property $ \n xs -> n <= 0 || n > length (xs :: [Integer]) || Lists.nth n xs == Just (xs !! (n-1))
it "for negative indexes it returns Nothing" $
property $ \n xs -> n > 0 || Lists.nth n (xs :: [Integer]) == Nothing
it "for too large indexes it returns Nothing" $
property $ \n xs -> n <= length xs || Lists.nth n (xs :: [Integer]) == Nothing
The first one thus excludes n <= 0 (negative or zero indices) as well as n > length xs and thus checks if the value is Just (xs !! (n-1)).
In the second case excludes values greater than zero, and checks if all remaining indices map on Nothing.
Finally the last property checks that for values that are higher than length xs, we obtain nothing as well.
Note that here nth uses one-based indexing. I leave it as an exercise to make it zero-based.

SML - error in finding elements in list

I'm new to SML. I've written a function which takes 2 int and a list of tuples as input.
fun move(x,y,mylist:(int * int)list): NOxNO =
let
val counter = ref y-1
in
if y=1 then (x,y)
else (
while !counter > 0 do (
if List.exists (fn s => s = (x,!counter)) mylist
then counter := !counter - 1
else break
);
if !counter = 0 then (x,y) else (x,y-1)
)
end
I may have syntax error since I'm a beginner. What the function is trying to do is: it will check the list to find all the tuples whose first element is x and second element varies from 1 to y-1 (tuples like this: (x,1) (x,2) ... (x,y-1) ) and if all of them exist in the list it will return (x,y) else (x,y-1). I used a while loop and a counter. counter is set to y-1 at first and in while loop if (x,counter) was found, counter's value will decrease. At last if counter=0 it means we have found all the tuples. After running the program I encountered this error:
Caught Error ../compiler/TopLevel/interact/evalloop.sml:296.17-296.20
../compiler/TopLevel/interact/evalloop.sml:44.55
../compiler/TopLevel/interact/evalloop.sml:66.19-66.27
What's wrong?
Here's some feedback:
(Error) As Andreas Rossberg said, break doesn't exist. But if you use (), the loop won't terminate when y > 1 and the predicate evaluates to false. You probably want to "break" by setting counter := 0 or counter := -1, depending on what you want the subsequent if !counter = 0 ... to do.
(Error) As Andreas Rossberg said, ref y-1 gives the following type error:
! Toplevel input:
! val r = ref y-1;
! ^
! Type clash: expression of type
! int
! cannot have type
! int ref
This is because function application (ref y) binds tighter than infix operators (y-1). What you mean is ref (y-1), since you can't subtract 1 from a reference.
This isn't very comprehensible or robust. I tried to run it in the simplest case I could think of,
val test1 = move (1,1,[])
But that's a weird base case not handled by the loop. If I change the numbers slightly,
val test2 = move (5,6,[])
then it returns either (5,6) or (5,5) depending on what you change break into.
Based on your description below the code, here is a suggested implementation, although I'm still not completely certain I understand the use of this function:
(* verticalPointsExist (x, y, ps) checks that
* (x,1), (x,2), ..., (x,y-1) are all in ps. *)
fun verticalPointsExist (_, 0, _) = true
| verticalPointsExist (x, y, ps) = List.exists (fn p => (x,y) = p) ps
andalso verticalPointsExist (x, y - 1, ps)
fun move (x, y, ps) =
if verticalPointsExist (x, y, ps) then (x,y) else (x,y-1)
Considerations I made:
Use recursion rather than iteration.
Split the checking part into a helper function, so move doesn't do two things.
Give the functions good names so the code reads more easily. Since I don't know the domain and am really guessing as to whether y is some kind of vertical dimension, there are probably even better names out there. (verticalLineExists? verticalPathClear?) Maybe a more general function will have a better name, e.g. one that took two points and saw that the line is clear between them.
There is no break in ML. You probably just want to write () there. Also, you'll need parens around the argument to ref in line 3.

Standard ML Palindrome without reversing the list

I am new to SML and don't understand too about the syntax.
I am doing a practice on checking palindrome without reversing the list. Here is my code
fun symmetric(i,n,inlist) =
if List.nth(inlist,i-1) = List.nth(inlist,n-i)
then true
else
false;
fun palindrome(n, inlist) =
let
val i = ref 1
in
while !i < n do (
if symmetric(!i,!n,!inlist) = false
then false
else ()
i := !i + 1
)
true
end;
I got errors in fun palindrome only, but can't fix it by myself.
You can even make a palindrome checker without converting your string to a list:
fun palindrome s =
let fun check i j =
i >= j orelse
String.sub (s, i) = String.sub (s, j) andalso
check (i+1) (i-1)
in check 0 (String.size s - 1) end
Here is some feedback for your code:
Naturally, consider using recursion rather than iteration.
A common problem for non-functional programmers is that they seem to want to execute many statements in succession only for their side-effect. In functional programming you rely very much on the value of every expression to guide the result of the program. There is a ; operator, though, and it could be used like this:
fun palindrome s =
let val i = ref 0
val j = ref (String.size s - 1)
val result = ref true
in while !i < !j do
(if String.sub (s, !i) = String.sub (s, !j)
then (i := !i + 1 ; j := !j - 1)
else (i := !j ; result := false))
; !result
end
Often, though, if you want to do multiple things in a row, let-expressions are just as neat as the ; operator.
The code
if A = false
then false
else B
can be written as
if not (A)
then false
else B
which can further be improved into
if A
then B
else false
which is really the same as
A andalso B
So the morals are:
Instead of A = false, write not A (and instead of A = true, write A).
You can always replace if ... then <true/false> else <true/false> with some combination of andalso and orelse. That is, if-then-else is never necessary when the result type is bool (but you might still prefer it if the logic is very convoluted).
If the restriction against reversing a list was intended to ban using the built-in rev but not computations which implicitly reverse lists, here is a stack-based approach. The idea is to push characters onto a stack (represented as a list), and then pop them off, checking them against the original list of characters. If either the stack or the original list empty first, or if the item popped doesn't match the corresponding char in the original list -- it isn't a plalindrome
fun pushAll [] stack = stack
| pushAll (x::xs) stack = pushAll xs (x::stack)
fun popCheck [] [] = true
| popCheck [] _ = false
| popCheck _ [] = false
| popCheck (x::xs) (y::ys) = x = y andalso popCheck xs ys
fun palindrome s =
let val chars = explode s
val stack = pushAll chars []
in
popCheck chars stack
end;

Check list for consecutive values within range (F#)

In a sorted list of 10 numbers, I want to find out whether any 5 consecutive numbers are within a certain range. For reference: This is called finding a "stellium" (astronomical term, regarding positions of planets).
If the list is:
let iList = [15; 70; 72; 75; 80; 81; 120; 225; 250; 260]
I want a function
let hasStellium iStellSize iStellRange iList
that will return
hasStellium 5 20 iList = true
The list is already sorted, so I could just proceed with clunky if-then statements (like "Check whether element 1 and 5 are less than 20 units apart, check whether element 2 and 6 satisfy the condition" etc.
let hasStellium iStellSize iStellRange iList=
if
iList.[iStellSize-1] - iList.[0] < iStellRange ||
iList.[iStellSize] - iList.[1] < iStellRange
then true
else false
But there must be a more elegant way, that also allows for other stellium sizes without having to manually add if-then lines.
Thank you very much for your help!
(If the function could return the index number where the stellium starts, even better)
Just combining two standard library functions:
let hasStellium iStellSize iStellRange iList =
iList |> Seq.windowed iStellSize
|> Seq.tryFindIndex (fun s -> (s.[iStellSize - 1] - s.[0] < iStellRange))
returns either None if no such range can be found, otherwise Some x where x - range beginning index.
Here you go. It returns an int option which is the start index of the range, or None if not found.
let tryFindStelliumIndex iStellSize iStellRange iList =
let rec findRange i n = function
| _ when (n + 1) = iStellSize -> Some (i - n)
| prev::cur::tail when (cur - prev) < iStellRange -> findRange (i + 1) (n + 1) (cur::tail)
| _::tail -> findRange (i + 1) 0 tail
| _ -> None
findRange 0 0 iList
Another variant using Seq functions:
let hasStellium size range l =
Seq.zip l (l |> Seq.skip (size - 1))
|> Seq.tryFindIndex (fun p -> snd p - fst p < range)
Had to hack in an "early return" with a mutable variable, but here is a rough version
let mutable found = false
for i = 0 to (iList |> List.length - iStellSize) do
if iList.[i + iStellSize] - iList.[i] <= iStellRange then //Did you mean < or <=?
found <- true
found