I have a list of trees that have associated numerical values (we can treat it like sorting a list of Ints for this case) and I want to sort the whole list from highest to lowest and return the new sorted list. How do I do this? I have encountered the built in list sorting functions such as sortBy but I don't understand how to implement it.
I'm also new to coding in general if it wasn't obvious ;)
Basically, you can write your function
score :: Tree -> Int
providing a score for each of your trees. The score above is an Int, but any other ordered type can do as a score.
Then, you can use
sortBy (flip $ comparing score) myListOfTree
to sort according to the score. Remember to import Data.Ord for comparing.
Note that this can compute the score more than once. If your score function is expensive, you might want instead to precompute the scores as follows:
map snd . sortBy (flip $ comparing fst) . map (\t -> (score t, t)) $ myListOfTree
Another option is to define your own comparison function directly
compareTree :: Tree -> Tree -> Ordering
compareTree t1 t2 | someCondition = LT -- t1 < t2
| someOtherCondiction = EQ -- t1 == t2
| otherwise = GT -- t1 > t2
where in the conditions you can use your score function, if you want.
Then use sortBy compareTree to do sort in ascending order, or sortBy (flip compareTree) for the descending sort.
If this ordering it the only "natural" ordering for your Tree type, you can even declare it such:
instance Ord Tree where
compare = compareTree
(Note however that this approach will call compareTree many times, one for every comparison, possibly computing the score over and over. Hence, the map solution shown above might still be preferred.)
Related
How do you check if two maps are equal in OCaml no matter the order ?
For example :
Map 1 :
var1 -> [Neg]
var2 -> [Pos; Zero; Neg]
var3 -> [Neg; Pos]
Map2 :
var1 -> [Neg]
var2 -> [Pos; Zero; Neg]
var3 -> [Pos; Neg]
What I've tried :
Map.equal (fun x y -> x = y)
This returns false, as it considers the order is important.
Should I sort the lists, or is there a more efficient functional way ?
Thanks
The document for Map.equal says:
equal cmp m1 m2 tests whether the maps m1 and m2 are equal,
that is, contain equal keys and associate them with equal data.
cmp is the equality predicate used to compare the data associated
with the keys.
You are specifically asking to use = as your equality predicate, but you're also saying that it's not what you want.
The solution is to supply the equality predicate that you do want.
If you want to know whether two lists contain the same elements regardless of order, you can sort the lists and compare the sorted values.
let equal_contents a b =
List.sort compare a = List.sort compare b
You can also keep the values in the map in a canonical order (i.e., sorted). Then the code you give above will work.
I am writing a function that will take a list of list and merge it into sorted pairs of list. For example [[1],[9],[8],[7],[4],[5],[6]] would return [[1,9],[7,8],[4,5],[6]]. This is my first attempt at SML. I keep getting this error: operator and operand don't agree [overload conflict].
fun mergePass[] = []
| mergePass(x::[]) = x::[]
| mergePass(x::y::Z) =
if x<y
then (x # y)::mergePass(Z)
else (y # x)::mergePass(Z);
Edit: If mergePass is called on [[1,9],[7,8],[4,5],[6]] I will need it to return [[1,7,8,9],[4,5,6]].
This merge function takes two sorted lists
fun merge([],y) = y
| merge(x,[]) = x
| merge(a::x,b::y) =
if a < b then a::merge(x,b::y)
else b::merge(a::x,y);
You seem reasonably close. A few hints/remarks:
1) Aesthetically, using nil in one line and [] in others seems odd. Either use all nil or use all []
2) Since the input are lists of lists, in x::y::z, the identifiers x and y would be lists of integers, rather than individual integers. Thus, x<y wouldn't make sense. You can't compare lists of integers using <.
3) Your problem description strongly suggests that the inner-lists are all 1-element lists. Thus you could use the pattern [x]::[y]::z to allow you to compare x and y. In this case, x#y could be replaced by [x,y]
4) If the inner lists are allowed to be of arbitrary size, then your code needs major revision and would probably require a full-fledged sort function to sort the result of concatenating pairs of inner lists. Also, in this case, the single list in the one inner list case should probably be sorted.
5) You have a typo: mergeP isn't mergePass.
On Edit:
If the sublists are each sorted (and the name of the overall function perhaps suggests this) then you need a function called e.g. merge which will take two sorted lists and combine them into a single sorted list. If this is for a class and you have already seen a merge function as an example (perhaps in a discussion of merge-sort) -- just use that. Otherwise you will have to write your own before you write this function. Once you have the merge function, skip the part of comparing x and y and instead have something as simple as:
| mergePass (xs::ys::zss) = (merge xs ys) :: mergePass zss
If the sublists are not merged, then you will need a full-fledged sort in which case you would use something like:
| mergePass (xs::ys::zss) = sort(xs # ys) :: mergePass zss
So I'm trying to write some minimal code to put two lists of strings together, and to do this I thought it was best to use the haskell map function.
Essentially I want to be able to do adders ["1","2"] ["3","4"] = ["1","2","3","4"]
So I have a function called adder, which takes a list, then adds a string to that list and returns the new list. Then I have a function called adders which replicates the adder function, but adds a list of strings instead of just one string, however at the moment it produces multiple lists instead of one list.
I thought
adder :: [String] -> String -> [String]
adder y x = y ++ [x]
adders y x = map (adder y) x
would work, but this just gives a list of two lists
[["1","2","3"],[["1","2","4"]]
How is the best way to go about this?
I thought it was best to use the haskell map function
No. map f applies f to every element of your list. But you don't want to change the elements at all, you want to change the list itself. That, however, is out of scope of the things that are possible with map. map cannot add more elements, neither can it remove some.
If you want to concatenate two lists, simply use ++:
adders :: [a] -> [a] -> [a]
adders x y = x ++ y
Is there a more efficient way to update an element in a list in Elm than maping over each element?
{ model | items = List.indexedMap (\i x -> if i == 2 then "z" else x) model.items }
Maybe Elm's compiler is sophisticated enough to optimize this so that map or indexedMap isn't unnecessarily copying over every element except 1. What about nested lists?
Clojure has assoc-in to update an element inside a nested list or record (can be combined too). Does Elm have an equivalent?
More efficient in terms of amount of code would be (this is similar to #MichaelKohl's answer):
List.take n list ++ newN :: List.drop (n+1) list
PS: if n is < 0 or n > (length of list - 1) then the new item will be added before or at the end of the list.
PPS: I seem to recall that a :: alist is slightly better performing than [a] ++ alist.
If you mean efficient in terms of performance/ number of operations:
As soon as your lists get large, it is more efficient to use an Array (or a Dict) instead of a List as your type.
But there is a trade-off:
Array and Dict are very efficient/ performant when you frequently retrieve/ update/ add items.
List is very performant when you do frequent sorting and filtering and other operations where you actually need to map over the entire set.
That is why in my code, List is what I use a lot in view code. On the data side (in my update functions) I use Dict and Array more.
Basically, an Elm list is not meant for such a use-case. Instead, consider using an Array. Array contains a set function you can use for what is conceptually an in-pace update. Here's an example:
import Html exposing (text)
import Array
type alias Model = { items : Array.Array String }
model =
{ items = Array.fromList ["a", "b", "c"]
}
main =
let
m = { model | items = Array.set 2 "z" model.items }
z = Array.get 2 m.items
output = case z of
Just n -> n
Nothing -> "Nothing"
in
text output -- The output will be "z"
If for some reason you need model.items to be a List, note that you can convert back and forth between Array and List.
I'm not overly familiar with Elm, but given that it's immutable by default, I'd assume it uses structural sharing for its underlying data structures, so your concern re memory may be unfounded.
Personally I think there's nothing wrong with your approach posted above, but if you don't like it, you can try something like this (or List.concat):
List.take n list ++ newN :: List.drop (n+1)
I'm definitely not an Elm expert, but a look at Elm's List documentation did not reveal any function to update the element at a given index in a list.
I like Michael's answer. It's quite elegant. If you prefer a less-elegant, recursive approach, you can do something like the following. (Like I said, I'm not an Elm expert, but hopefully the intention of the code is clear if its not quite right. Also, I don't do any error handling.)
updateListAt :: List a -> Int -> a -> List a
updateListAt (head :: tail) 0 x = x :: tail
updateListAt (head :: tail) i x = head :: (updateListAt tail (i - 1) x)
However, both the runtime and space complexity will be O(n) in both the average and worst cases, regardless of the method used. This is a consequence of Elm's List being a single-linked list.
Regarding assoc-in, if you look at the Clojure source, you'll see that assoc-in is just recursively defined in terms of assoc. However, I think you'd have trouble typing it for arbitrary, dynamic depth in Elm.
I have a list of lists like so:
[["BBBBBBBB",
"BWFFFPFGB",
"BWFFFPFGB",
"BWFFMPFGB",
"BWFFFPF_B",
"BWFFFPF6B",
"BBBBBBB"]]
I've done a little research and have found out how to access individual elements using the !! operator. But when it comes to searching for a certain element 'M' I'm not sure how to go about that. My friend said I need to use something like (x:xs):xss on a list, but when I try this in the WinGHCi haskell program I get this.
Prelude> let list = [["BBBBBBBB",
"BWFFFPFGB",
"BWFFFPFGB",
"BWFFMPFGB",
"BWFFFPF_B",
"BWFFFPF6B",
"BBBBBBB"]]
Prelude> head(x:xs):xss
<interactive>:192:2: Not in scope: `x'
<interactive>:192:4: Not in scope: `xs'
<interactive>:192:8: Not in scope: `xss'
I understand that I declare the name as list and not x:xs but even when I declare it as x:xs I still get the errors. I'm probably still a little new to haskell to really understand what to do so I may be going about this way wrong.
I've looked here Replace individual list elements in Haskell? because eventually I want to replace the M with something different but I'm not completely sure how I would implement that.
Any help/guidance is appreciated, thanks!
First let's see how to replace a W with M
charWM :: Char -> Char
charWM 'W' = 'M' -- If you see W, put M.
charWM x = x -- If you see anything else, put it back as is.
You can rewrite that function how you like by adding other letter transformations.
Now let's make that work over a list. There's a great function map :: (a ->b) -> [a] -> [b] that lets you apply a function on every element on a list.
stringWM :: String -> String
stringWM xs = map charWM xs -- do charWM to everything in xs.
For example stringWM "QWERTY WILL WIN" = "QMERTY MILL MIN"
Next we can do that to a list of lists:
lolWM :: [String] -> [String]
lolWM xss = map stringWM xss
(String is a type synonym for [Char].)
Let's test that out in ghci:
*Main> list'
["BBBBBBBB","BWFFFPFGB","BWFFFPFGB","BWFFMPFGB","BWFFFPF_B","BWFFFPF6B","BBBBBBB"]
*Main> lolWM list'
["BBBBBBBB","BMFFFPFGB","BMFFFPFGB","BMFFMPFGB","BMFFFPF_B","BMFFFPF6B","BBBBBBB"]
All good.
Your example wasn't exactly list', it was [list'] which has 1 element, so to work on that we'd need to map lolWM. Often we wouldn't bother writing stringWM or lolWM and go directly to lists of lists of lists, if that's what we needed:
lololWM = (map.map.map) charWM
map.map.map means map the map of the map. You can allow that to blow your mind a little, or you can just say list of list of list of Char, so map map map - one map per list level.
In the future, maybe you'll want to replace W with Strings instead of characters.
rewriteChar :: Char -> String
rewriteChar 'W' = "--^--"
rewriteChar x = [x] -- put x in a list to make it a string
This time, map isn't enough: map rewriteChar "QWERTY WILL WIN" gives
["Q","--^--","E","R","T","Y"," ","--^--","I","L","L"," ","--^--","I","N"]
We could use concat on that to flatten it into a single list, but it's more fun to do
rewriteString = concatMap rewriteChar
So now rewriteString "QWERTY WILL WIN" give us "Q--^--ERTY --^--ILL --^--IN".
For more mindblowing things to try, there's "QWERTY WILL WIN" >>= rewriteChar and "Hello Mum" >>= \x -> [x,x,x]
First of all, virtually all "variables" in Haskell are immutable, so there's no "changing a list", there are modified copies.
Second, you need to find an element by some criteria. To do that, you need to traverse a list. - This can be done using recursion. Filtering can be done using a function passed as an argument of your traversing function (this function must take an element and return a boolean value).
Try to put the above together and make your own function. Start with a type signature, it shows what you want to do: to take a list of Char (it's better to generalize to a generic type) and a function which possibly changes an element and return a modified list:
replaceFunc :: (Char -> Char) -> String -> String
Also, read http://www.haskell.org/haskellwiki/How_to_work_on_lists , there's a hint there how to apply some function to specific elements only.