Related
I have the following question "Given a list of integer pairs, write a function to return a list of even numbers in that list in sml".
this is what I've achieved so far
val x = [(6, 2), (3, 4), (5, 6), (7, 8), (9, 10)];
fun isEven(num : int) =
if num mod 2 = 0 then num else 0;
fun evenNumbers(list : (int * int) list) =
if null list then [] else
if isEven(#1 (hd list)) <> 0
then if isEven(#2 (hd list)) <> 0
then #1 (hd list) :: #1 (hd list) :: evenNumbers(tl list)
else []
else if isEven(#2 (hd list)) <> 0
then #1 (hd list) :: evenNumbers(tl list)
else [];
evenNumbers(x);
the result should be like this [6,2,4,6,8,10]
any help would be appreciated.
I see two obvious problems.
If both the first and second number are even, you do
#1 (hd list) :: #1 (hd list) :: evenNumbers(tl list)
which adds the first number twice and ignores the second.
If the first number is odd and the second even, you do
#1 (hd list) :: evenNumbers(tl list)
which adds the number that you know is odd and ignores the one you know is even.
Programming with selectors and conditionals gets complicated very quickly (as you've noticed).
With pattern matching, you could write
fun evenNumbers [] = []
| evenNumber ((x,y)::xys) = ...
and reduce the risk of using the wrong selector.
However, this still makes for complicated logic, and there is a better way.
Consider the simpler problem of filtering the odd numbers out of a list of numbers, not pairs.
If you transform the input into such a list, you only need to solve that simpler problem (and there's a fair chance that you've already solved something very similar in a previous exercise).
Exercise: implement this transformation. Its type will be ('a * 'a) list -> 'a list.
Also, your isEven is more useful if it produces a truth value (if you ask someone, "is 36 even?", "36" is a very strange answer).
fun isEven x = x mod 2 = 0
Now, evenNumbers can be implemented as "just" a combination of other, more general, functions.
So running your current code,
- evenNumbers [(6, 2), (3, 4), (5, 6), (7, 8), (9, 10)];
val it = [6,6,3,5,7,9] : int list
suggests that you're not catching all even numbers, and that you're catching some odd numbers.
The function isEven sounds very much like you want to have the type int -> bool like so:
fun isEven n =
n mod 2 = 0
Instead of addressing the logic error of your current solution, I would like to propose a syntactically much simpler approach which is to use pattern matching and fewer explicit type annotations. One basis for such a solution could look like:
fun evenNumbers [] = ...
| evenNumbers ((x,y)::pairs) = ...
Using pattern matching is an alternative to if-then-else: the [] pattern is equivalent to if null list ... and the (x,y)::pairs pattern matches when the input list is non-empty (holds at least one element, being (x,y). At the same time, it deconstructs this one element into its parts, x and y. So in the second function body you can express isEven x and isEven y.
As there is a total of four combinations of whether x and y are even or not, this could easily end up with a similarly complicated nest of if-then-else's. For this I might do either one of two things:
Use case-of (and call evenNumbers recursively on pairs):
fun evenNumbers [] = ...
| evenNumbers ((x,y)::pairs) =
case (isEven x, isEven y) of
... => ...
| ... => ...
Flatten the list of pairs into a list of integers and filter it:
fun flatten [] = ...
| flatten ((x,y)::pairs) = ...
val evenNumbers pairs = ...
I have the following file with the predicate attends which symbolizes that every student attends a certain course
(the first argument : Student_ID, the second argument : Course_ID).
attends(476, c216).
attends(478, c216).
attends(484, c216).
attends(487, c216).
attends(491, c216).
What I want to do is create a predicate-function which creates an examination schedule consisting of 3 lists (A,B,C) with courses.
Each list symbolizes a week. Then for instance to find the optimal schedule in order it suits most students, it prints out all the different permutations of courses in pairs of 3-3-2:
The list A is week one with courses [c204,c209,c210] in the first case bellow.
List B is week 2 etc...
?- schedule(A,B,C).
A = [c204,c209,c210],
B = [c212,c214,c216],
C = [c217,c218];
A = [c204,c209,c210],
B = [c212,c214,c216],
C = [c218,c217];
Problem 1:
So how can I take the attends/2 predicates and convert only the second argument to a List, in a manner that the list will contain all the courses that have been declared.
For example: L = [c212,c213...].
Problem 2:
The permutations will be done with a custom function called k_permutation/3:
delete(E,L,NL):-
append(L1,[E|L2],L),
append(L1,L2,NL).
k_permutation(0,_,[]).
k_permutation(K,L1,[X|T2]) :-
K > 0,
K1 is K - 1,
delete(X,L1,L2),
k_permutation(K1,L2,T2).
But for some reason this custom function (k_permutation/3) runs for infinite time. Is there something wrong with the functions recursion?
How should the function be used?
Well as for problem 1 the easy way would be:
collect_courses(L1):- findall(Course, attends(_,Course), L), sort(L,L1).
L will have all courses that appear in attends/2, so it will have duplicates, that's the reason we're using sort/2 which removes duplicates.
As for problem 2, first of all Swi-Prolog already has a definition of delete/3 predicate so I suggest that you rename it. Apart from that the k_permutations/2 works fine:
?- k_permutation(2,[1,2,3],L).
L = [1, 2] ;
L = [1, 3] ;
L = [2, 1] ;
L = [2, 3] ;
L = [3, 1] ;
L = [3, 2] ;
false.
I came away from Professor Frisby's Mostly Adequate Guide to Functional Programming with what seems to be a misconception about Maybe.
I believe:
map(add1, Just [1, 2, 3])
// => Just [2, 3, 4]
My feeling coming away from the aforementioned guide is that Maybe.map should try to call Array.map on the array, essentially returning Just(map(add1, [1, 2, 3]).
When I tried this using Sanctuary's Maybe type, and more recently Elm's Maybe type, I was disappointed to discover that neither of them support this (or, perhaps, I don't understand how they support this).
In Sanctuary,
> S.map(S.add(1), S.Just([1, 2, 3]))
! Invalid value
add :: FiniteNumber -> FiniteNumber -> FiniteNumber
^^^^^^^^^^^^
1
1) [1, 2, 3] :: Array Number, Array FiniteNumber, Array NonZeroFiniteNumber, Array Integer, Array ValidNumber
The value at position 1 is not a member of ‘FiniteNumber’.
In Elm,
> Maybe.map sqrt (Just [1, 2, 3])
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm
The 2nd argument to function `map` is causing a mismatch.
4| Maybe.map sqrt (Just [1, 2, 3])
^^^^^^^^^^^^^^
Function `map` is expecting the 2nd argument to be:
Maybe Float
But it is:
Maybe (List number)
Similarly, I feel like I should be able to treat a Just(Just(1)) as a Just(1). On the other hand, my intuition about [[1]] is completely the opposite. Clearly, map(add1, [[1]]) should return [NaN] and not [[2]] or any other thing.
In Elm I was able to do the following:
> Maybe.map (List.map (add 1)) (Just [1, 2, 3])
Just [2,3,4] : Maybe.Maybe (List number)
Which is what I want to do, but not how I want to do it.
How should one map over Maybe List?
You have two functors to deal with: Maybe and List. What you're looking for is some way to combine them. You can simplify the Elm example you've posted by function composition:
> (Maybe.map << List.map) add1 (Just [1, 2, 3])
Just [2,3,4] : Maybe.Maybe (List number)
This is really just a short-hand of the example you posted which you said was not how you wanted to do it.
Sanctuary has a compose function, so the above would be represented as:
> S.compose(S.map, S.map)(S.add(1))(S.Just([1, 2, 3]))
Just([2, 3, 4])
Similarly, I feel like I should be able to treat a Just(Just(1)) as a Just(1)
This can be done using the join from the elm-community/maybe-extra package.
join (Just (Just 1)) == Just 1
join (Just Nothing) == Nothing
join Nothing == Nothing
Sanctuary has a join function as well, so you can do the following:
S.join(S.Just(S.Just(1))) == Just(1)
S.join(S.Just(S.Nothing)) == Nothing
S.join(S.Nothing) == Nothing
As Chad mentioned, you want to transform values nested within two functors.
Let's start by mapping over each individually to get comfortable:
> S.map(S.toUpper, ['foo', 'bar', 'baz'])
['FOO', 'BAR', 'BAZ']
> S.map(Math.sqrt, S.Just(64))
Just(8)
Let's consider the general type of map:
map :: Functor f => (a -> b) -> f a -> f b
Now, let's specialize this type for the two uses above:
map :: (String -> String) -> Array String -> Array String
map :: (Number -> Number) -> Maybe Number -> Maybe Number
So far so good. But in your case we want to map over a value of type Maybe (Array Number). We need a function with this type:
:: Maybe (Array Number) -> Maybe (Array Number)
If we map over S.Just([1, 2, 3]) we'll need to provide a function which takes [1, 2, 3]—the inner value—as an argument. So the function we provide to S.map must be a function of type Array (Number) -> Array (Number). S.map(S.add(1)) is such a function. Bringing this all together we arrive at:
> S.map(S.map(S.add(1)), S.Just([1, 2, 3]))
Just([2, 3, 4])
Right now I have an SML function:
method([1,1,1,1,2,2,2,3,3,3]);
returns:
val it = [[2,2,2],[3,3,3]] : int list list
but I need it to return:
val it = [[1,1,1,1],[2,2,2],[3,3,3]] : int list list
This is my current code:
- fun method2(L: int list) =
= if tl(L) = [] then [hd(L)] else
= if hd(tl(L)) = hd(L) then hd(L)::method(tl(L)) else [hd(L)];
- fun method(L: int list) =
= if tl(L) = [] then [] else
= if hd(tl(L)) = hd(L) then method(tl(L)) else
= method2(tl(L))::method(tl(L));
As you can see it misses the first method2 call. Any ideas on how I can fix this? I am completely stumped.
Your problem is here if hd(tl(L)) = hd(L) then method(tl(L)) else. This is saying if the head of the tail is equal to the head, then continue processing, but don't add it to the result list. this will skip the first contiguous chunk of equal values. I would suggest separating the duties of these functions a bit more. The way to do this is to have method2 strip off the next contiguous chunk of values, and return a pair, where the first element will have the contiguous chunk removed, and the second element will have the remaining list. For example, method2([1, 1, 1, 2, 2, 3, 3]) = ([1, 1, 1], [2, 2, 3, 3]) and method2([2, 2, 3, 3]) = ([2, 2], [3, 3]). Now, you can just keep calling method2 until the second part of the pair is nil.
I'm not quite sure what you are trying to do with your code. I would recommend creating a tail recursive helper function which is passed three arguments:
1) The list of lists you are trying to build up
2) The current list you are building up
3) The list you are processing
In your example, a typical call somewhere in the middle of the computation would look like:
helper([[1,1,1,1]], [2,2],[2,3,3,3])
The recursion would work by looking at the head of the last argument ([2,3,3,3]) as well as the head of the list which is currently being built up ([2,2]) and, since they are the same -- the 2 at the end of the last argument is shunted to the list being built up:
helper([[1,1,1,1]], [2,2,2],[3,3,3])
in the next step in the recursion the heads are then compared and found to be different (2 != 3), so the helper function will put the middle list at the front of the list of lists:
helper([[2,2,2], [1,1,1,1]], [3],[3,3])
the middle list is re-initialized to [3] so it will start growing
eventually you reach something like this:
helper([[2,2,2], [1,1,1,1]], [3,3,3],[])
the [3,3,3] is then tacked onto the list of lists and the reverse of this list is returned.
Once such a helper function is defined, the main method checks for an empty list and, if not empty, initializes the first call to the helper function. The following code fleshes out theses ideas -- using pattern-matching style rather than hd and tl (I am not a big fan of using those functions explicitly -- it makes the code too Lisp-like). If this is homework then you should probably thoroughly understand how it works and then translate it to code involving hd and tl since your professor would regard it as plagiarized if you use things you haven't yet studied and haven't made it your own work:
fun helper (xs, ys, []) = rev (ys::xs)
| helper (xs, y::ys, z::zs) =
if y = z
then helper(xs, z :: y :: ys, zs)
else helper((y::ys)::xs,[z],zs);
fun method [] = []
| method (x::xs) = helper([],[x],xs);
I'm new in Prolog and I have some problem understanding how the recursion works.
The think I want to do is to create a list of numbers (to later draw a graphic).
So I have this code :
nbClassTest(0, _).
nbClassTest(X, L) :-
numberTestClass(A,X),
append([A], L, L),
X is X - 1,
nbClassTest(X, L).
But it keeps giving me 'false' as an answer and I don't understand why it doesn't fill the list. It should end if X reaches 0 right?
The numberTestClass(A,X), gives me a number (in the variable A) for some X as if it was a function.
You should build the list without appending, because it's rather inefficient.
This code could do:
nbClassTest(0, []).
nbClassTest(X, [A|R]) :-
numberTestClass(A, X),
X is X - 1,
nbClassTest(X, R).
or, if your system has between/3, you can use an 'all solutions' idiom:
nbClassTest(X, L) :-
findall(A, (between(1, X, N), numberTestClass(A, X)), R),
reverse(R, L).
the problem is that you use the same variable for the old and the new list. right now your first to append/3 creates a list of infinite length consisting of elements equal to the value of A.
?-append([42],L,L).
L = [42|L].
?- append([42],L,L), [A,B,C,D|E]=L.
L = [42|L],
A = B, B = C, C = D, D = 42,
E = [42|L].
then, if the next A is not the same with the previous A it will fail.
?- append([42],L,L), append([41],L,L).
false.
there is still on more issue with the code; your base case has an non-instantiated variable. you might want that but i believe that you actually want an empty list:
nbClassTest(0, []).
nbClassTest(X, L) :-
numberTestClass(A,X),
append([A], L, NL),
X is X - 1,
nbClassTest(X, NL).
last, append/3 is kinda inefficient so you might want to avoid it and build the list the other way around (or use difference lists)
It fails because you use append in wrong way
try
nbClassTest(0, _).
nbClassTest(X, L) :-
numberTestClass(A,X),
append([A], L, Nl),
X is X - 1,
nbClassTest(X, Nl).
append concatenate 2 lists so there is no such list which after adding to it element still will be same list.