Is it possible to write functions that returns functions whose signature depends on the arguments to the builder function?
Specifically I am refining an implementation of primitive recursion I wrote. I want to have factory like functions that for numeric parameters generate a function that works on tuples or lists of the length equal to the arguments passed for the numeric parameters. Currently I am handling cases like pr 1 2 [0,5,13], which is an invalid statement from the perspective of primitive recursion, at runtime through Either:
pr :: (Show a) => Int -> Int -> [a] -> Either String a
pr i k args
| 1 <= i && i <= k && length args == k = Right $ args!!(i-1)
| i <= 0 = Left "first argument of pr needs to be greater or equal to 1"
| k < i = Left "first argument of pr needs to be lesser or equal to the second argument"
| length args /= k = Left $ "pr expected "++(show k)++" arguments, got "++(show $ length args)++": pr "++(concat[show i, " ", show k, " ", show args])
But I would like to somehow catch that case at compile time, as from the perspective of the formal system I want to implement with this, this is a compile time error -- passing more arguments to a function than its domain specifies.
Is this somehow possible and if not what would be the correct approach to get compile time errors for what should be invalid statements.
What you want is a sized vector. It is like a list but in addition to the type of its elements, it is also parametrised by type level natural numbers.
sized-vector package on Hackage is what you need. As it happens, the function you're trying to implement is the last function in this library.
Note that every time you call last you will have to prove the compiler that its argument vector is of size at least 1. You can do this by constructing the vector in the source code (for example, the compiler will understand 1 :- 2 :- Nil is of size 2) or if the vector is obtained at runtime perhaps by conversion from a list, you'll have to write a function that either gives a run time error if it has no elements or constructs a vector of size at least one i.e. have the type level size S n for some n.
If you're not familiar with dependently typed programming (a paradigm that includes this and much much more) I suggest you look through some tutorials first. For example, this post is a good example that includes how to implement vectors from scratch and write functions for them.
A word of caution, learning and using dependently typed programming is exciting, addictive, but also time consuming. So if you want to focus on the task at hand, you might like to live with runtime checks for now.
Related
i am a beginner in ocaml and I am stuck in my project.
I would like to count the number of elements of a list contained in a list.
Then test if the list contains odd or even lists.
let listoflists = [[1;2] ; [3;4;5;6] ; [7;8;9]]
output
l1 = even
l2 = even
l3 = odd
The problem is that :
List.tl listoflists
Gives the length of the rest of the list
so 2
-> how can I calculate the length of the lists one by one ?
-> Or how could I get the lists and put them one by one in a variable ?
for the odd/even function, I have already done it !
Tell me if I'm not clear
and thank you for your help .
Unfortunately it's not really possible to help you very much because your question is unclear. Since this is obviously a homework problem I'll just make a few comments.
Since you talk about putting values in variables you seem to have some programming experience. But you should know that OCaml code tends to work with immutable variables and values, which means you have to look at things differently. You can have variables, but they will usually be represented as function parameters (which indeed take different values at different times).
If you have no experience at all with OCaml it is probably worth working through a tutorial. The OCaml.org website recommends the first 6 chapters of the OCaml manual here. In the long run this will probably get you up to speed faster than asking questions here.
You ask how to do a calculation on each list in a list of lists. But you don't say what the answer is supposed to look like. If you want separate answers, one for each sublist, the function to use is List.map. If instead you want one cumulative answer calculated from all the sublists, you want a fold function (like List.fold_left).
You say that List.tl calculates the length of a list, or at least that's what you seem to be saying. But of course that's not the case, List.tl returns all but the first element of a list. The length of a list is calculated by List.length.
If you give a clearer definition of your problem and particularly the desired output you will get better help here.
Use List.iter f xs to apply function f to each element of the list xs.
Use List.length to compute the length of each list.
Even numbers are integrally divisible by two, so if you divide an even number by two the remainder will be zero. Use the mod operator to get the remainder of the division. Alternatively, you can rely on the fact that in the binary representation the odd numbers always end with 1 so you can use land (logical and) to test the least significant bit.
If you need to refer to the position of the list element, use List.iteri f xs. The List.iteri function will apply f to two arguments, the first will be the position of the element (starting from 0) and the second will be the element itself.
I need writing a function which takes as input
a = [12,39,48,36]
and produces as output
b=[4,4,4,13,13,13,16,16,16,12,12,12]
where the idea is to repeat one element three times or two times (this should be variable) and divided by 2 or 3.
I tried doing this:
c=[12,39,48,36]
a=size(c)
for i in a
repeat(c[i]/3,3)
end
You need to vectorize the division operator with a dot ..
Additionally I understand that you want results to be Int - you can vectorizing casting to Int too:
repeat(Int.(a./3), inner=3)
Przemyslaw's answer, repeat(Int.(a./3), inner=3), is excellent and is how you should write your code for conciseness and clarity. Let me in this answer analyze your attempted solution and offer a revised solution which preserves your intent. (I find that this is often useful for educational purposes).
Your code is:
c = [12,39,48,36]
a = size(c)
for i in a
repeat(c[i]/3, 3)
end
The immediate fix is:
c = [12,39,48,36]
output = Int[]
for x in c
append!(output, fill(x/3, 3))
end
Here are the changes I made:
You need an array to actually store the output. The repeat function, which you use in your loop, would produce a result, but this result would be thrown away! Instead, we define an initially empty output = Int[] and then append! each repeated block.
Your for loop specification is iterating over a size tuple (4,), which generates just a single number 4. (Probably, you misunderstand the purpose of the size function: it is primarily useful for multidimensional arrays.) To fix it, you could do a = 1:length(c) instead of a = size(c). But you don't actually need the index i, you only require the elements x of c directly, so we can simplify the loop to just for x in c.
Finally, repeat is designed for arrays. It does not work for a single scalar (this is probably the error you are seeing); you can use the more appropriate fill(scalar, n) to get [scalar, ..., scalar].
I am trying to solve an exercise in SML like ;
Write an ML program to be used for a multiple choice exam containing 10 questions. Your program should include the following:
o The answer key of the exam
e.g.
val key= “adabcbaadb”;
o Type definition for a record (info) which contains name and answers of a student.
e.g. {name=”Ali”,ans=”abadccdadb”}
o Type definition for a tuple (result) which contains name and score of a student.
e.g. ("Ali",60)
o Write as many functions as needed to take a list of records of type info containing the information of 3 students, calculate their scores, and convert them into a list of tuples of type result. You can write other supporting functions as well
e.g.
- val stuInfo:info list=[{name=”Ali”,ans=”abadccdadb”},
{name=”Ege”,ans=”cbbdacabda”},
{name=”Can”,ans=”adabcbaadb”}];
- val results = calculate(stuInfo);
val results = [("Ali",60),("Ege",20),("Can",100)] : result list
o Write as many functions as needed to calculate the average score, and return the students who received a score above the average.
e.g.
-val aboveList=aboveAvg(results);
val aboveList = [("Ali",60),("Can",100)] : result list
Notes:
Make sure you give the types of your parameters and return value in all your functions.
Paranthesize your expressions.
Use explode function which converts a string to a character array
e.g.
- explode "abc";
val it = [#"a",#"b",#"c"] : char list
My written code for this exercise is below ; but my code is not working :/ Where is my mistake ?
val answer_key="ddacbadbca";
type student_information={s_name:string,s_choice:string};
type student_result=string*int;
val student:info list=[{s_name="David",s_choice="adcbbaccad"},{s_name="John",s_choice="ccdabdbbcc"},{s_name="Alice",s_choice="abdaccacdb"}];
val 3studentsResult:student_result=average(student_information);
fun average ((h::t):student_information list):student_result list=student_score(explode"#ans h",explode"key")::average(t);
val sum=0;
fun student_score(((a::b,c::d):'a list):'a list) = (if(a=c) then sum=sum+10 else sum=sum+0 )::student_score(b,d);
Thanks
There are multiple issues about this code. Before starting, I should recommend you to work on your styling, you can use some spaces and extra lines between functions to see what you are doing easily, like this:
val answer_key = "ddacbadbca";
type student_information = {s_name:string, s_choice:string};
type student_result = string * int;
val student : student_information list =
[{s_name="David", s_choice="adcbbaccad"},
{s_name="John", s_choice="ccdabdbbcc"},
{s_name="Alice", s_choice="abdaccacdb"}];
You might also want to keep your variable and type names consistent. I think the convention is to use snake case (like snake_case) for type names and camel case (like camelCase) for variable and function names.
Here are some of your actual mistakes:
For your student variable, you give the type info list, which doesn't exist in your program. I assume your student_information variable used to be named info, so you should change one of those to the other.
You have a variable named 3studentsResult. Variables cannot start with numbers, they have to start with letters. You have to rename that variable, to something like threeStudentsResult.
In your 3studentsResult variable, you are using the average function that you define later. That doesn't work in Standard ML. You should define a function before using it. (Unless you need mutual recursion, you can use the and keyword then, but it's irrelevant to your problem.) Therefore, define average before 3studentsResult, and define student_score before average.
Even after fixing these, your student_score function is incorrect. I assume sum=sum+10 means that you are trying to change sum's value, like you would do in an imperative programming language. However, Standard ML is a functional language and changing values of variables is something you should avoid in functional programming. In fact, when I talk about "variables" in SML, I mean value declarations that cannot be changed. I recommend you to think about your problem a little bit more and maybe you can divide your problem into sub-problems. Good luck.
If I have a function
let rec function n =
if n<0 then []
else n-2 # function n-2 ;;
I get an error saying that the expression function n-2 is a list of int but it is expecting an int.
How do I concatenate the values to return all the n-2 values above zero as a list?
I cannot use the List module to fold.
Thanks
Your title asks how to concatenate lists, but your question seems rather different.
To concatenate lists, you can use the # operator. In many cases, code that depends on this operator is slower than it needs to be (something to keep in mind for later :-).
Here are some things I see wrong with the code you give:
a. You can't name a function function, because function is a keyword in OCaml.
b. If you use the # operator, you should have lists on both sides of it. As near as I can see, the thing on the left in your code is not a list.
c. Function calls have higher precedence than infix operators. So myfun n - 2 is parsed as (myfun n) - 2. You probably want something closer to myfun (n - 2).
Even with these changes, your code seems to generate a list of integers that are 2 apart, which isn't what you say you want. However, I can't understand what the function is actually supposed to return.
It seems like you are not concatenating lists, but concatenating ints instead. This is done by the :: operator. So your code would look like:
else (n-2)::(fun (n-2))
Although I could see this function possibly not producing the desired output if you put in negative numbers. For example if you pass through n = 1, n-2 will evaluate to -1 which is less than zero.
I would need to get the position of an element in may array of type Array Int Int. I found the method elemIndex or find to get the position. My problem is, that I don't need the prefix Just 5 for example. So how I only get the number 5 in my example?
The principle
To safely extract a value from a Maybe a value, you can either use pattern matching, like so:
case elemIndex 'C' list of
Just n -> "You can find C at position " ++ show n
Nothing -> "There is no C in the list."
This will return something like
"You can find C at position 2"
or
"There is no C in the list."
depending on whether or not there is a C in the list.
Making it convenient
Of course, this kind of pattern matching is unwieldy to write all the time, so there exists a function called maybe that does pretty much the same thing. If you look at its type signature, you see that
maybe :: b -> (a -> b) -> Maybe a -> b
So it takes a "default value" of type b, and a function from a to b, and will return a b. Whether or not this is the default value depends on whether or not the Maybe a value exists or is Nothing. For example, if you want to check if a list element is allowed entry into an 18+ club, you can do
maybe False (\n -> n >= 18) (elemIndex 'C' list)
This will say False if the index is less than 18 or if the element doesn't exist in the list. If it does exist, it will check if it's greater or equal to 18, and then return True.
Keeping the Just
What I've told you so far is how to get rid of the Just in a safe way. Sometimes, you can't get rid of the Just just yet – sometimes you have no sensible value to return if you have a Nothing on your hands instead of the Just. What you can do then is manipulate values when they are still inside the Just. For example, to subtract 15 from a value inside a just, you just do
fmap (subtract 15) (Just 23)
which will return
Just 8
So you see how fmap sort of takes a Just something value and applies the function to the something part of it, keeping the Just outside. If you would do
fmap (subtract 15) Nothing
it would just keep the Nothing, so the result would be
Nothing
Making it unsafe (kids, don't try this at home!)
Maybe is great because it is an error handling system that forces you to Do Things Right. You just can't ignore the possibility of an error (represented by Nothing.) Another common error handling system is terrible with this. That system is the system of exceptions. Nobody will know if you blatantly ignore that an exception can occur, which is a basis for very unsafe programs.
So you really want to keep the Just until you can toss it away and at the same time replace a potential Nothing value with something sensible.
If you can guarantee that there is no possibility of a Nothing value. If you know for sure that everytime you call elemIndex the element is going to be somewhere in the list, then it's okay to use fromJust. fromJust will blindly try to take a value out of a Just, without giving a dang about what happens if there is no Just there. fromJust will simply explode your program (throw an exception) if something went wrong.
As you understand, you have to use it with much care.
Being unsafe with style
However, as Jedai points out in a comment, even if you shouldn't be able to get a Nothing value, it is better to be explicit about it. Instead of using fromJust, consider doing something like
fromMaybe (error "The input " ++ show list ++ " shouldn't create a Nothing value!")
(elemIndex 'C' list)
which will blow up with a very specific error message, pinpointing where something must have gone wrong.
This is of course the same thing as the pattern match that looks like
case elemIndex 'C' list of
Just n -> n
Nothing -> error "The input " ++ show list ++ " shouldn't create a Nothing value!"
only compacted into the standard fromMaybe function.
Use fromJust from Data.Maybe. Like:
fromJust $ elemIndex 2 [1,2,3,4,5]
And you will get only 1.
But, this will fail if you don't have desired element in the list and you'll get an exception:
*** Exception: Maybe.fromJust: Nothing