How can I make my code more modular in SML? - sml

I am programming in SML. My function takes an integer and then splices it into a list with a comma. For example digits 12345 -> [1,2.3,4,5]. My question is how do I make my code more modular. I kind of hardcoded my code. I would like it to work for an infinite amount of integers.
fun digits(m:int) =
if m <10 then
[m]
else if m < 100 then
[m div 10] # [m mod 10]
else if m > 100 andalso m < 1000 then
[(m div 10) div 10] #[(m div 10) mod 10] # [m mod 10]
else if m > 1000 andalso m < 10000 then
[((m div 10) div 10) div 10] # [((m div 10) div 10) mod 10] # [(m div 10) mod 10] # [m mod 10]
else if m > 10000 andalso m < 100000 then
[(((m div 10) div 10) div 10) div 10] # [((m div 10) div 10) div 10 mod 10] # [((m div 10) div 10) mod 10] # [(m div 10) mod 10] # [m mod 10]
else if m > 100000 andalso m < 1000000 then
[((((m div 10) div 10) div 10) div 10) div 10] # [(((((m div 10) div 10) div 10) div 10) mod 10) mod 10] # [((m div 10) div 10) div 10 mod 10] # [((m div 10) div 10) mod 10] # [(m div 10) mod 10] # [m mod 10]
else if m > 1000000 andalso m < 10000000 then
[(((m div 10) div 10) div 10) div 10 div 10 div 10] # [(((m div 10) div 10) div 10) div 10 div 10 mod 10] # [((m div 10) div 10) div 10 mod 10]# [((m div 10) div 10) mod 10] # [(m div 10) mod 10] # [m mod 10]
else
[(m div 10) div 10] # [(m div 10) mod 10] # [m mod 10]

Think about the problem you're trying to solve. There's no way you can hard-code all the possibilities.
Let's consider a basic example: a single digit number. Clearly for any input less than 10 the result is just that number in a list.
fun digits m =
if m < 10 then
[m]
If the number is greater than 10, it could be two digits, or it could be more. We don't know. But can we find the smallest digit?
fun smallestDigit m =
if m < 10 then
m
else
m mod 10
So if we call smallestDigit 123 we get 3.
And if we 123 div 10 we get 12. Turns out we get 2 if we call smallestDigit 12. We just need to repeat this until we've got all of our digits. In functional programming, this repetition is typically achieved with recursion.
So let's put that together using recursion. What's critical is that we have an exit condition that stops the recursion, and that the other case converges toward that exit condition.
fun digits m =
if m < 10 then
[m]
else
let
val smallestDigit = m mod 10
val remainingDigits = m div 10
in
digits remainingDigits # [smallestDigit]
end

Related

SML: syntax error: replacing LET with RAISE

I'm writing a function, that takes date in (d, m, y) format. I need to count rez value, all of these +getNthInt function calls are adding certain elements from the list.
fun firstNewMoonInt ((d, m, y) : int * int * int) : int option =
let
if m = 1 orelse m = 2 then y - 1
else y
val rez = newStyleCorrection (d, m, y) * 100000
+ getNthInt(thousandCorrection, y div 1000)
+ getNthInt (hundredCorrection, y div 100 mod 10)
+ getNthInt (decadeCorrection, y mod 100 div 10)
+ getNthInt (yearCorrection, y mod 1000)
+ getNthInt (monthCorrection, m - 1)
+ getNthInt (calendarCorrection, y mod 4)
rez - lastSmaller(rez - 100000, reductions)
in
if rez div 100000 <= 30 then SOME rez
else NONE
end
I'm getting two syntax errors:
2.3-2.6 Error: syntax error: replacing LET with RAISE
13.3 Error: syntax error: inserting LET
Since I use all keywords for the constructions: let-in-end, if-then-else. I don't understand, what is wrong with my code?
Immediately inside a let, there should be a sequence of declarations. (Declarations are things like val x = ... or fun f x = ...). But in your code, there is an if which begins an expression.
You could fix this by making a new variable which is the result of the if expression:
let
val new_y =
if m = 1 orelse m = 2 then y - 1
else y
val rez = ...
And then you will need to figure out where to use new_y in the rest of the code.
Note that there is a similar problem just a few lines further down:
rez - lastSmaller(rez - 100000, reductions)
This is an expression where there should be another declaration. You could also fix it the same way: val new_rez = rez - lastSmaller (...) and then use new_rez where appropriate below that.

Write the function residue l n which calculates the residue of n by l by making an iterative calculation

I want to write the function residue l n which calculates the residue of n by l by making an iterative calculation starting with n and using the items in the list in order. The calculation is as follows:
-initially the residue is the value of n
-each element e of l (taken in the order of the list) changes the residue in the following way:
if e and the residue are of the same parity (both even or both odd) then the new residue is the sum of r and e, otherwise it is the difference between r and e (r-e).
-the last residue is the result of the game.
Example: residu [1;3] 7 returns 5 as a result of the following calculations:
7 + 1 (same parity +) = 8
8 - 3 (parité différente -) = 5
This is my code but it doesn't seem to be working:
let rec residue l n =
if l = [] then 0 else
if (((List.hd l) mod 2 <> 0) && (n mod 2 <> 0 )) || (((List.hd l) mod 2 == 0) && (n mod 2 == 0 ))
then
(List.hd l) + residue (List.tl l) ((List.hd l)+ n) else
n - (List.hd l) - residue (List.tl l) (n - (List.hd l));;
residu [1;3] 7;;
- : int = 6 (The correct result should be 5)
Thank you for your help.
Here is my stab at it.
let rec residue l n =
let parity a b =
if ((a mod 2 <> 0) && (b mod 2 <> 0)) ||
((a mod 2 == 0) && (b mod 2 == 0)) then true else false in
match l, n with
| [], n -> n
| (x::xs), n -> if parity x n then residue xs (n+x) else residue xs (n-x)
Hope it helps for figuring out the problem.

How to Keep Non-Reduced Form of Ratio in Haskell

I am trying to create a function rationalSumLowest that takes an Int n and returns a list of ratio numbers that sum to n, but only showing the ratio numbers that are already in reduced form. I have attempted to do this by creating the following functions:
mergeArrays :: [Int] -> [Int] -> [Ratio Int]
mergeArrays [] [] = []
mergeArrays (x:xs) (y:ys) = [x%y] ++ mergeArrays xs ys
rationalSum :: Int -> [Ratio Int]
rationalSum 0 = []
rationalSum 1 = []
rationalSum n = mergeArrays [1..(n-1)] [(n-1), (n-2)..1]
sumLowestBool :: Ratio Int -> Bool
sumLowestBool x
|(gcd (extractNumerator (x)) (extractDenominator (x)) == 1) = True
|otherwise = False
rationalSumLowest :: Int -> [Ratio Int]
rationalSumLowest 0 = []
rationalSumLowest 1 = []
rationalSumLowest n = [i | i <- rationalSum (n), sumLowestBool i == True]
It should run like this:
*Main> rationalSumLowest 5
[1 % 4,2 % 3,3 % 2,4 % 1]
*Main> rationalSumLowest 8
[1 % 7,3 % 5,5 % 3,7 % 1]
*Main> rationalSumLowest 12
[1 % 11,5 % 7,7 % 5,11 % 1]
With rationalSum running like this:
*Main> rationalSum 5
[1 % 4,2 % 3,3 % 2,4 % 1]
*Main> rationalSum 8
[1 % 7,1 % 3,3 % 5,1 % 1,5 % 3,3 % 1,7 % 1]
*Main> rationalSum 1
[]
My rationalSum function works properly, but my rationalSumLowest function does not. It returns the same list as the rationalSum function. I know this is because in Haskell, all ratios are automatically reduced and thus my sumLowestBool would return True for all entries in the list.
My question is: is there a way to keep the non-reduced form of the ratio in the original list so that my sumLowestBool function can work properly?
Since the ratios are reduced, checking that the gcd is 1 is the same as checking that the numerator hasn't changed.
import Data.Ratio
rationalSumLowest :: Integer -> [Rational]
rationalSumLowest n =
[ v
| i <- [1 .. n-1]
, let v = i % (n-i)
, numerator v == i
]

SML Create a function that finds numbers not divisible from list

Write a function N that for a
given natural number r (2 in the example), a list of natural numbers x1..xn,
and a list of natural numbers q1..qm (n, m ≥ 0), returns a
list of length n whose i’th element is a list of the natural
numbers in the interval [xi − r, xi + r] that are not
divisible by any of the numbers in q1..qm.
- N 2 [25, 50, 90, 11] [2,3,7];
val it = [[23,25] , [] , [89] , [11, 13]] :
int list list
You could define the function as follows:
fun divisible (q::qs, x) = if (x mod q = 0) then true else divisible(qs, x)
| divisible (nil, x) = false;
fun N r xs qs = let
val cs = map(fn x => List.tabulate(1+2*r, fn y => x + y - r))(xs);
in
map(fn bs => List.filter(fn x => not (divisible(qs,x))) (bs))(cs)
end;
For the given example it gives:
- N 2 [25, 50, 90, 11] [2,3,7];
val it = [[23,25],[],[89],[11,13]] : int list list

Error in nested list comprehension in Haskell code

I am trying to write the following list comprehension in Haskell and it doesn't typecheck. I am new at this and can't really figure out why.
something :: Int -> [Int]
something n = [[ 2 * x + 1 | x <- xs]|xs <- [3..n],i <- [1..],j <-[1..] ,xs == i+j+2*i*j,i<=j,i>=1]
This is what I see:
Couldn't match expected type `Int' with actual type `[t0]'
In the expression: [2 * x + 1 | x <- xs]
NB: There could be a lot more wrong with this piece of code.
Here's what I am really trying to learn to do. From a list of all naturals from 3 to n(which is the Int input to the function), I want to extract just the subset of numbers which can be written as i+j+2*i*j where i, j are integers and the i<=j and i>=1. To this subset list , I want to apply the function 2*x+1 to each element x and output a final list.
Hope that makes sense.
First of all you have a nested list comprehension, so you're creating a list of lists, so the return type should be [[Int]] (list of lists of ints) and not [Int] (list of ints).
Second of all xs is a number (because you take it out of a list of numbers), but its name suggests that it's a list and when you do x <- xs you're actually treating it as if it was a list.
In response to your edit: I don't really see why you thought you needed nested list comprehensions for this. If we just remove the nesting from your code, we get something that is pretty close to working (I've also renamed xs to x because calling a number xs is just confusing - I've also removed the condition that i is at least 1 because that's already a given since you take i from the list [1..]):
[ 2 * x + 1 | x <- [3..n], i <- [1..],j <-[1..] ,x == i+j+2*i*j,i<=j]
Now this compiles, but it will loop forever. Why does it loop forever? Because you take i and j from infinite lists. This means it will start with x=3, i=1, j=1 and then try all values for j from 1 to infinity before it will try the next value of i (so in other words it will never try the next value of i).
So what we need to do is to give i and j upper bounds. An easy upper bound to pick is x (if i or j are greater than x (and neither is smaller than 1), i+j+2*i*j can't possibly be equal to x), so you get:
[ 2 * x + 1 | x <- [3..n], i <- [1..x],j <-[1..x], x == i+j+2*i*j, i<=j]
This works, but it can still be simplified a bit by somewhat: If we take j from the list [i..n] instead of [1..n], we guarantee that j is at least i and we don't need the condition i<=j anymore, so we can write:
[ 2 * x + 1 | x <- [3..n], i <- [1..x], j <-[i..x], x == i+j+2*i*j]
PS: Doing it this way (iterating over all x and then iterating over all possible values of i and j for each x) is a bit inefficient, so you might reconsider your approach. On the other hand if all you need is something that works, this is fine.
First, a function name must not be upper-case.
xs <- [3..n] means that xs is an Int, but x <- xs uses it as a list.
The rest of the comprehension looks a little bit strange, too. If you care to explain what exactly you want to do, we might be able to help a little bit more. :-)
[Edit]
You get an infinite list of your numbers by using [i+j+2*i*j| j <-[2..], i<-[1..(j-1)]], but it's not sorted. [x| x <-[3..(2*n*n)], j <-[2..n], i<-[1..(j-1)], x==i+j+2*i*j] gives a sorted list of all such numbers smaller than 2n².
Let's start with what you've got.
something n = [[ 2 * x + 1 | x <- xs]|xs <- [3..n],i <- [1..],j <-[1..] ,xs == i+j+2*i*j,i<=j,i>=1]
The basic problem here is that you don't need a nested list comprehension.
something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [1..], x == i+j+2*i*j, i<=j, i>=1]
This will compile. But there is, as you suspect, a lot more wrong with this piece of code.
Let's start with the conditions. Testing for i>=1 is superfluous, given that i <- [1..].
something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [1..], x == i+j+2*i*j, i<=j]
Similarly, we can get rid of the i<=j condition if we start j off at i instead of at 1.
something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [i..], x == i+j+2*i*j]
It should be clear that values of j greater than (n - i) `div` (1 + 2 * i) cannot possibly result in an x ≤ n.
something n = [ 2 * x + 1 | x <- [3..n], i <- [1..], j <- [i .. (n - i) `div` (1 + 2 * i)], x == i+j+2*i*j]
Similarly, values of i of n `div` 3 or above cannot possibly result in an x ≤ n.
something n = [ 2 * x + 1 | x <- [3..n], i <- [1 .. (n `div` 3) - 1], j <- [i .. (n - i) `div` (1 + 2 * i)],
x == i+j+2*i*j]
At this point we have done enough for something to actually generate results. But there are duplicate values (e.g. when (i,j) is either (1,7) or (2,4) you get x = 22), which I assume you don't want.
We filter them out using nub from Data.List.
something n = nub [ 2 * x + 1 | x <- [3..n], i <- [1 .. (n `div` 3) - 1],
j <- [i .. (n - i) `div` (1 + 2 * i)], x == i+j+2*i*j]
There's no need to check that x satisfies a condition when we could construct x to satisfy that condition in the first place. (You will want to satisfy yourself that 3 ≤ x ≤ n still.) This is more efficient.
something n = nub [ 2 * x + 1 | i <- [1 .. (n `div` 3) - 1], j <- [1 .. (n - i) `div` (1 + 2 * i)],
let x = i+j+2*i*j]
The results are no longer coming out in ascending order, so let's make sure they do.
something n = sort $ nub [ 2 * x + 1 | i <- [1 .. (n `div` 3) - 1], j <- [1 .. (n - i) `div` (1 + 2 * i)],
let x = i+j+2*i*j]
On a style point, the doubling and adding one is a separate calculation from ensuring that x can be expressed as i+j+2*i*j, so let's split them up.
something n = sort $ map f $ nub [ x | i <- [1 .. (n `div` 3) - 1], j <- [1 .. (n - i) `div` (1 + 2 * i)],
let x = i+j+2*i*j]
where f x = 2 * x + 1
This allows us to get rid of x from the list comprehension.
something n = sort $ map f $ nub [ i+j+2*i*j | i <- [1 .. (n `div` 3) - 1],
j <- [1 .. (n - i) `div` (1 + 2 * i)]]
where f x = 2 * x + 1
Done.