I can't use patterns or folding, per the parameters of the assignment, of which this is a toy example of a particular approach to a larger problem.
When I run the code, I get a '0', naturally. So the question is, how do I get the final value of a_count?
fun num_counter(numbers: int list, a_number: int) =
let val count = 0
in
let fun count_num(numbers: int list, a_count: int) =
if null numbers
then 0
else if (hd numbers) = a_number
then count_num(tl numbers, count + 1)
else count_num(tl numbers, count)
in
count
end
end
There are several issues with your code:
Your recursive function count_num is never called.
Your recursion terminates by returning 0 instead of the result
you've acumulated so far (a_count).
There is some confusion between parameter a_count that, as I
understood holds the number of occurences of a_number and count
declared at the second line.
Here is some correction to it:
fun num_counter(numbers: int list, a_number: int) = let
fun count_num(numbers: int list, count: int) =
if null numbers
then count (* reached the end of the list =>
returned the number of occurences computed *)
else if (hd numbers) = a_number
then count_num(tl numbers, count + 1)
else count_num(tl numbers, count)
in
count_num (numbers, 0) (* first call of count_num,
count initiated to 0 *)
end;
Also note that you can use pattern matching to enhance readability of
your recursive function:
fun num_counter(numbers: int list, a_number: int) =
let fun count_num([], count) = count
| count_num(i :: tl, count) =
count_num(tl, if i = a_number then count + 1 else count)
in
count_num (numbers, 0)
end;
You can write it shorter using a fold:
fun num_counter (numbers, a_number) =
let fun count (b_number, total) =
if a_number = b_number
then total + 1
else total
in foldl count 0 numbers
end
Here foldl takes three arguments: the function count that accumulates the result when visiting each number, b_number in numbers, the initial value for total being 0 and the numbers to fold over, numbers. When foldl has visited the last number, it uses the last accumulation in total as the result.
foldl is itself defined like this:
fun foldl f e [] = e
| foldl f e (x::xr) = foldl f (f(x, e)) xr
Or you can filter and take the length which costs a bit more:
fun num_counter (numbers, a_number) =
length (filter (fn b_number => a_number = b_number) numbers)
Related
In SML is it possible to find the number of occurrences of the min number in a list?
I have code to find the number of occurrences of a number but i am stumped on how to find the min and use it to find how many of the minimum num there is.
fun occurrences(nil, n)=0
| occurrences(ls, n) =
if hd(ls)=n then occurrences(tl(ls),n) + 1
else occurrences(tl(ls),n) + 0;
Thank you!
You can write a function that keeps track of the min value and its count as you iterate through the list.
We can do this by implementing a tail-recursive function which helper, which maintains the value of the current minimum and a count of the number of times that item has appeared.
We can then wrap this in another function min_count via a let-in-end block.
For example:
fun min_count [] = 0 (* the empty list has zero items in it *)
| min_count (x :: xs) =
let
(* when we reach the end of the list, return the accumulated count *)
fun helper (_, n) [] = n
| helper (m, n) (y :: ys) =
(* if we find a new minimum, reset the count *)
if y < m then helper (y, 1) ys
(* if the current list item is larger than the min, ignore it *)
else if y > m then helper (m, n) ys
(* if we've found another instance of the min, add one to the count *)
else helper (m, n + 1) ys
in
(* first item appears once *)
helper (x, 1) xs (* first item appears once *)
end;
This problem is a good test for using folds on a list.
Finding the mininum
If we want to find the minimum in a list we need to iterate over the list checking each element against a predetermined starting minimum value. If that element is less than that known minimum, we continue to iterate using that value instead. When we're done, we have the minimum value.
If the list is empty, there is no minimum value. If only one value is in the list, the minimum is obviously that. If there are more values, the starting minimum value is the first element.
We can use foldl to handle the iteration in this last case.
fun min([]) = NONE
| min([x]) = SOME x
| min(first::rest) =
SOME (foldl (fn (x, min) => if x < min then x else min)
first rest)
Finding occurrences
You've already done this, but this can be done in terms of a fold as well.
fun occurrences(lst, v) =
foldl (fn (x, count) => if x = v then count + 1 else count)
0 lst
Putting this together
We could use these two functions to find the number of times the minimum occurs in a list.
let
val numbers = [1, 4, 7, 2, 9, 0, 1, 6, 0]
val min = min(numbers)
val occ = case min of
NONE => NONE
| SOME x => SOME (occurrences(numbers, x))
in
case (min, occ) of
(NONE, NONE) => print("No minimum found.")
| (SOME m, SOME t) => print("Min: " ^ Int.toString(m) ^ "; times: " ^ Int.toString(t))
end
Can we do it in a single pass?
Using the above approach, we have to iterate over the list twice. This is a more general, but less efficient way of getting both pieces of information the minimum and the number of occurrences of it. We can use foldl to get both pieces of information, and it's going to look at lot like the definition of min.
We just need to pass a function to foldl that keeps a running tally of the number of times it has found the minimum value, and we need to pass it a tuple with both an initial minimum value and an initial count of 1.
fun minCount([]) = NONE
| minCount([x]) = SOME (x, 1)
| minCount(first::rest) =
SOME (foldl (fn (x, init as (min, count)) =>
case Int.compare(x, min) of
EQUAL => (min, count + 1)
| LESS => (x, 1)
| _ => init)
(first, 1)
rest)
With this function defined, our previous code can be rewritten as:
let
val numbers = [1, 4, 7, 2, 9, 0, 1, 6, 0]
val mc = minCount(numbers)
in
case mc of
NONE => print("No minimum found.")
| SOME (m, t) => print("Min: " ^ Int.toString(m) ^ "; times: " ^ Int.toString(t))
end
Assuming that you are supposed to use your occurrences function in the solution, write a function that finds the minimum,
fun minimum [x] = x
| minimum (x::xs) = let
val min = minimum xs
in
if x < min then x else min
end
Note that this does not handle the empty list.
You need to decide whether to leave the missing pattern as a runtime error, or add it and handle the error, for instance by raising an exception or by changing the return type to int option.
If you're taking a course, use one of the methods you've learned so far.
Then you can use that function,
occurrences(the_list, minimum the_list)
I'm trying to trim a list of random numbers so that sum of the numbers in [0,1] in the resulting smaller list accumultates to a value under 1.
This is interesting in a sense that average of these list prefix lengths is e, somehow.
While getting the length of prefix I encountered a problem - I managed to get the program to work on a determined infinite list, or a slice of random list, but the program hangs on infinite random list. What am I doing wrong?
import System.Random
-- Count list items that accumulate to 1.
count :: (Num a, Ord a) => [a] -> Int
count xs = 1 + length xs'
where xs'= takeWhile (< 1) $ scanl1 (+) xs
-- Works of infinite list
a = (return (repeat 0.015)) :: IO [Double]
fa = fmap count a
--67
-- Works on trimmed list of randoms
rio = randomRIO (0::Double, 1)
b = sequence $ take 10 (repeat rio)
fb = fmap count b
-- fb is usually 2 to 5
-- Hangs on infinite list of randoms
c = sequence (repeat rio)
fc = fmap count c
-- fc hangs... ;(
You can define an IO action to create an infinite stream of ranged random numbers like so:
import System.Random
randomRIOs :: (Random a) => (a, a) -> IO [a]
randomRIOs (a, b) = randomRs (a, b) <$> newStdGen
After which, the following works fine with your definition of count:
main = do
n <- count <$> randomRIOs (0::Double, 1)
print n
You can't really have an infinite list of random numbers, because randomness is too strict. So, you can't have the call to sequence outside of your call to count. One obvious thing you could try would be a partial reimplementation of sequence inside of count itself:
count :: (Num a, Ord a, Monad m) => [m a] -> m (Maybe Int)
count = go 0 0
where go n total [] = pure Nothing
go n total (x:xs) = do
num <- x
let total' = total + num
n' = succ n
if total' >= 1
then pure $ Just n'
else go n' total' xs
I've also amended the result to return a Maybe, because it seems wrong to return 1 for an empty list (as yours did), or return the length of a list even if its elements sum to something less than 1.
Another reasonable change would be to not accept [m a], but just a single m a, which can reliably produce a value any number of times. Then we don't have to worry about the input running out, and so no Maybe is needed:
count :: (Num a, Ord a, Monad m) => m a -> m Int
count m = go 0 0
where go n total = do
num <- m
let total' = total + num
n' = succ n
if total' >= 1
then pure n'
else go n' total'
Then you can simply call count $ randomRIO (0::Double, 1), and it will produce as many random numbers as are needed.
write a curried function f1 which takes a list and a positive number n as inputs
and checks whether any element occurs exactly n times in it.
eg.- f1 [1,2,1,3] 2;
val it = true : bool
- f1 [1,2,1,3] 3;
val it = false : bool
You need to count occurrences of each element in your list. You'll need a function which will count occurrences, and a function that will check each element of a list against list itself.
fun isThereCeratinNumberOfOccurences(lst, count) =
let
fun countOccurences(lst, what) =
if null lst
then 0
else if hd lst = what
then 1 + countOccurences(tl lst,what)
else countOccurences(tl lst,what)
fun checkEachElement(lst, elements, number) =
if null elements
then false
else if countOccurences(lst, hd elements) = count
then true
else checkEachElement(lst, tl elements, count)
in
checkEachElement(lst, lst, count)
end
I'm trying to iterate a list and square all the number and add them together
sumsq (x:xs) =
let total = 0
loop length(x:xs) (x:xs) total
loop 0 (x:xs) = return ()
loop n (x:xs) total =
do
let
sq = ((x:xs)!!n)^2
total = total + sq
loop ((n-1) (x:xs) total)
But I'm getting parse error in loop. Where am I going wrong?
Also is there a better way to do this?
First of all - you miss spaces! It is significant.
Second, you forget in from let ... in. We could not use in in do-notation:
sumsq (x:xs) =
let total = 0 in
loop length(x:xs) (x:xs) total
Third, you do not use x and xs form (x:xs) :
sumsq xs =
let total = 0 in
loop (length xs) xs total
And we unite our length xsin one block. It is fourth.
Fifth, we have 3, not 2 arguments for loop:
loop 0 xs total = return total
Sixth, (!!) work from 0, but you use it from 1, so (xs !! (n -1)) is right
Seventh, you don't need to use monad, just recursion. So, get rid from return and do
Eighth. you have infinite recursive total = total + smth
Ninth, we can't use arguments as tuple, so, you final working result is :
sumsq xs =
let total = 0 in
loop (length xs) xs total
loop 0 xs total = total
loop n xs total = loop (n-1) xs total1
where
sq = (xs !! (n -1)) ^2
total1 = total + sq
UPDATED
If we are talking about complexity, it is not good - O(n^2) as it is mentioned in comments : for each element we seek this element.
We could simplify our loop function and get rid of n argument:
loop [] total = total
loop (x:xs) total = loop xs total1
where
sq = x ^ 2
total1 = total + sq
and our sumsq function we write:
sumsq xs = loop xs 0
P.S.
This is an implementation much easier function sumsq = sum. map (^ 2)
If I understood you correctly, you could simply do this with map and sum:
Prelude> let myFun = sum . map (^2)
Prelude> myFun [1, 2, 3]
14
Or with foldl1 and lambda:
Prelude> let myFun' = foldl1 (\s x -> s + x^2)
Prelude> myFun' [1, 2, 3, 4]
30
Surely something like this would be the usual approach?
sumSquared :: [Integer] -> Integer
sumSquared [] = 0
sumSquared (x:xs) = (x * x) + sumSquared xs
Or you could do this even more succinctly with foldr, or sum and map (like #soon's answer)
The do must be more indented than the word loop.
Apart from that, you don't need do (or return) at all here, unless you can answer the question which monad this is for?
There are more problems with your code. One of the most severe is this:
You don't seem to know what "pattern matching" is, nor what it is good for. You really want to learn about it, otherwise you can't write any good programs.
I'm making a function that returns a list of fibonacci numbers; however I'm getting the error
7.25-7.37: mismatch on application: expression type
int list
does not match function's argument type
int list list
because type
int
does not unify with
int list
Here's my code:
fun fibl [] = [0]
| fibl [0] = [0]
| fibl [1] = [1]
| fibl n = fibl[n]
| fibl [n] =
let
val f = fn () => fibl [n - 1] + fibl [n - 2]
in
(f ())::[n]
end;
The interpreter says the error occurs in the lambda, but the way I understood it; writing fibl [n] means it checks for a list, and then when you later use n you're using the value it contains, so I shouldn't be calling fibl with a list of int lists but JUST a list of ints so I don't really know what went wrong here, anyone got a clue?
There are multiple issues with your code.
Starting with this line
| fibl n = fibl [n]
You are matching for a list of integers, in the two previous lines. Thus your function must take such a list as input. However in the above line, you are taking this list of integers n and calling your function recursively with n packed inside another list fibl [n].
Note however that this match will generate an infinite loop when fixed!
Another issue is that your last match is redundant, as the above match will catch all input, resulting in your last match newer being matched.
You also have some errors in the last match. Note that your function returns its result as a singleton list. In your function f, you try to add the result of your fibl function (i.e., an int list), which is possible.
Here you either have to "pull out" the number from the list or do something entirely different.
Since you are trying to generate a list of fibonacci numbers, I would suggest that you do something entirely different, as your current approach only generates a list of two fibonacci numbers.
I would suggest that you start off with a naive solution; having a function that will generate the n'th fibonacci number, and then generate a list by applying this function over and over.
Some examples
As requested, here are some examples of how one could do it naively.
First of we define a function that will generate the n'th fibonacci number.
fun fib 0 = 0
| fib 1 = 1
| fib n = fib (n-1) + fib (n-2)
This function can then easily be mapped through a list of numbers, generating a list of those specific fibonacci numbers
- val fibLst = map fib [0,1,2,3,4,5,6,7,8];
val fibLst = [0,1,1,2,3,5,8,13,21] : int list
However we can also create a function, that will generate a list 0...n and then apply the fib function to that list.
fun genFib n = List.tabulate (n, fib);
or without the List.tabulate function
fun genFib1 n =
let
fun loop 0 acc = fib 0 :: acc
| loop m acc = loop (m-1) (fib m :: acc)
in
loop (n-1) []
end
Obviously the fib function could be implemented much better, and our list generating function could just take advantage of how the next fibonacci number is created. You will notice that the below function is much faster at generating the list containing the first 40 fibonacci numbers.
fun genFib2 n =
let
fun loop 0 _ _ acc = rev acc
| loop m f_1 f_2 acc = loop (m-1) f_2 (f_1 + f_2) (f_2 :: acc)
in
loop (n-1) 0 1 [0]
end
The above have some issues with the size of an integer in SML (you can generate the 44th fibonacci number, but not 45th). So we can extend the fib function to use the arbitrary-precision integer IntInf, together with the idea from above
fun fibInf n : IntInf.int =
let
fun loop 0 f_1 _ = f_1
| loop m f_1 f_2 = loop (m-1) f_2 (f_1 + f_2)
in
loop n 0 1
end
Now it is both "fast" and possible to generate the first 100 fibonacci numbers, or more
fun genFibInf n = List.tabulate (n, fibInf);
- genFibInf 1000;
val it = [0,1,1,2,3,5,8,13,21,34,55,89,...] : IntInf.int list
- List.nth(it, 700);
val it =
8747081495575284620397841301757132734236724096769738107423043259252750#
: IntInf.int
Another function that generates a list of Fibonacci numbers up to the n-th:
fun fibs 0 = [0]
| fibs 1 = [1, 0]
| fibs n =
case fibs (n - 1) of
(n1::n2::ns) => (n1+n2::n1::n2::ns)
| _ => raise Fail "Not possible"
(Fix base case for definitions of Fibonacci numbers that start from (1, 1, ...) or (1, 2, ...).