Update tree in clojure - clojure

I have a tree (list of lists), I would like to modify a selected node of the tree.
[
[:a1]
[
[:b1, b2]
[:c1, c2]
]
]
For example, I would like to append b3 to the node (array) with b entries.
[
[:a1] ; 0
[ ; 1
[:b1, b2, b3] ; 1, 0
[:c1, c2] ; 1, 1
]
]
Question: how to update a node given list of indices where I can find the target node ([1, 0])? In other languages, with a mutable tree I could simply find the array, and do target_node.append("b3"), which is impossible in clojure.
I would like to avoid explicitly using mutable Java objects.
Related
Update hierarchical / tree structure in Clojure

update-in will do what you want:
user=> (def t [[:a1] [[:b1 :b2] [:c1 :c2]]])
#'user/t
user=> (update-in t [1 0] conj :b3)
[[:a1] [[:b1 :b2 :b3] [:c1 :c2]]]

Related

Iterate over a list of distances in Netlogo

I need help for the following issue.
Basically, I have four turtles and a list of distances among them, let's say [ 0 1 2 3 ]. Zero is the distance of a turtle from itself.
I want to obtein the following list [ 0, 1/5, 2/4, 3/3 ]. In other terms, I want to divide each number to the sum of all the other numbers. Can you help me?
The map primitive allows you to make a calculation for each item in a list separately and returns a new list of the results, as shown by the following examples from the Netlogo dictionary:
show map round [1.1 2.2 2.7]
=> [1 2 3]
show map [ i -> i * i ] [1 2 3]
=> [1 4 9]
Now applying this to your case, I let every item of the list be divided by the sum of all items in the list minus its own value:
to test
let the-list [ 0 1 2 3 ]
let total sum the-list
let new-list map [ x -> x / (total - x)] the-list
show new-list
;=> [0 0.2 0.5 1]
end

how can I add a list item into a list in Netlogo?

I have a list called first-list [].
anytime I produce other lists such as [2 3],[1 4 6],... I want to add these list into that first-list:
first-list :[[2 3],[1 4 6], ...].
how can I do that in NetLogo?
I know by lput I can add items but not lists into a list
of-course you can, for example:
let mylist [1 2]
set mylist lput [3 4] mylist
print mylist ; prints [1 2 [3 4]]
If you have variables, such as:
let x 42
let y "string"
to create a list with them, you need to use the built-in primitive "list", as such:
let mylist2 list x y
To create a list with variables that has more than 2 variables (or just 1), you need to encapsulate it with parentheses, such as:
let mylist3 (list x y x)
I strongly suggest you refer to the netlogo dictionary, in the link https://ccl.northwestern.edu/netlogo/docs/dictionary.html#list
You can only create a list using the "[" and "]" if you are using primitive numbers and strings, such as [1 2 "three"], but if you want to create a list with variables you need to use the "list" primitive, such as (list x y "a string" 5)

How to call a list for update multiple times until a condition is fulfilled in Netlogo

I am new to Netlogo and still learning , what i want to do is call a list after an update is done and do another update to it until a condition is reached , if a have a list
let xy [[-2 6][-1 6][0 6][-1 6][-2 6][1 5][2 5][-3 9][-4 9][-5 9][-6 9][-3 9]]
let same true
I am trying to remove from the list the first sublist between two same elements [-2 6 ][-1 6][0 6][-1 6][-2 6] then i also want to remove the sublist between the other two same elements [-3 9][-4 9][-5 9][-6 9][-3 9] until there are no more elements that repeat in this case result should be [[1 5] [2 5]] and i control this list after removing the sublists with the condition:
if length xy = length remove-duplicates xy [ set same false ]
I have already done the removal code below and it removes the first sublist but i might have a lot of sublists , and i want to know how after one removal can i get again the updated list (in these case i should somehow take the final-list in the code) and control it again with these condition.
I was thinking to do a to-report procedure and a while loop , for example (maybe i am wrong with this)
to-report update-list [list]
while [same != false ]
[ ; do the removal stuff
set list-xy item POSITION (FIRST MODES xy) xy xy
let first-pos POSITION (FIRST MODES xy) xy
set list-temp remove-item first-pos xy
set sec-pos position list-xy list-temp + 1
set sublist-1 sublist xy 0 first-pos
set sublist-2 sublist xy sec-pos length xy
set final-list sentence sublist-1 sublist-2
set xy final-list
; the condition if i don't have any duplicates the size of two lists should have the same size , no duplicates
if length xy = length remove-duplicates xy
[ set same false ]
]
report update-list xy
I am not sure what to report at the end of the procedure and how to recall the list again, so i can remove all those sublists.
Any ideas are appreciated, thank you
This is easiest to solve using recursion:
to-report remove-fenced-sublists [xs]
if empty? xs
[ report [] ]
let pos position first xs butfirst xs
if not is-number? pos
[ report fput first xs remove-fenced-sublists butfirst xs ]
report remove-fenced-sublists sublist xs (pos + 2) length xs
end
Sample run:
observer> show remove-fenced-sublists [[-2 6][-1 6][0 6][-1 6][-2 6][1 5][2 5][-3 9][-4 9][-5 9][-6 9][-3 9]]
observer: [[1 5] [2 5]]
If I understand your goal, your core need can be captured by building up a new list item by item but trimming it whenever a duplicate appears. For a single new item this is:
to-report trim-or-grow [#list #item]
let _i position #item #list
report ifelse-value (_i != false) [sublist #list 0 _i] [lput #item #list]
end
You can then reduce with this reporter:
to test
let xy [[-2 6][-1 6][0 6][-1 6][-2 6][1 5][2 5][-3 9][-4 9][-5 9][-6 9][-3 9]]
print reduce trim-or-grow fput [] xy
end

How do I get the longest continuous sequence in a matrix which fulfills a criterion?

I have a matrix in which every vector consists of hashmaps. Here's a toy example:
[
[{:label x, ...}, {:label y, ...}, ...]
[{:label y, ...}, {:label z, ...}, ...]
[{:label p, ...}, {:label x, ...}, ...]
...
[{:label x, ...}, {:label x, ...}, ...]
]
Because only the label is relevant to my problem, I have removed the other things.
Now, what I want to do is for each row, calculate the longest sequence of continuous labels. That is, if the labels of a row are A B B B A A C A, then the longest sequence is B B B. What I then want to return is a tuple of 1) which row k has the longest such sequence (any of the longest is fine in case of a tie), and also 2) what the index i of the first item in the sequence is, as well as 3) what the index j of the last item in the sequence is.
So, for this simplified matrix, that would be k = 1, i = 2, j = 5.
[
[A B B A A C]
[C B A A A A]
[B A C A B A]
]
I'm new to functional programming and I really like it so far, but I can't quite figure out how to do this without resorting to e.g. the foreach loops of my native php. I'm not looking for somebody to do everything for me, but a hint in the right direction would be very much appreciated. Thank you.
(def m "ABBAAAC")
(->> m (map-indexed vector)
(partition-by #(-> % second identity))
(sort-by count >)
(first))
Gives:
([3 \A] [4 \A] [5 \A])

How to change a value of sub maps of a map?

I am new to Clojure and functional programming and now I am stuck with a problem. I get such a data structure:
{
:service1 \a
:service2 \b
:service3 \c
:default \d
:alert-a {
:duration "00:00-23:59"
:if-alert true
:continuous-times 2
:time-interval [2 6 9 15 30 60]
:times -1
}
:alert-b {
:duration "09:00-23:00"
:if-alert true
:continuous-times 2
:time-interval [2 6 9 15 30 60]
:times -1
}
:alert-c {
:duration "00:00-23:59"
:if-alert true
:continuous-times 5
:time-interval [5]
:times 1
}
:alert-d {
:duration "00:00-23:59"
:if-alert true
:continuous-times 5
:time-interval [5 15 30 60]
:times -1
}
}
This is something read from a config file. I want to change all the :duration value to a DateTime object using clj-time. So I can get something like:
{
:service1 \a
:service2 \b
:service3 \c
:default \d
:alert-a {
:duration DateTime Object
:if-alert true
:continuous-times 2
:time-interval [2 6 9 15 30 60]
:times -1
}
:alert-b {
:duration DateTime Object
:if-alert true
:continuous-times 2
:time-interval [2 6 9 15 30 60]
:times -1
}
:alert-c {
:duration DateTime Object
:if-alert true
:continuous-times 5
:time-interval [5]
:times 1
}
:alert-d {
:duration DateTime Object
:if-alert true
:continuous-times 5
:time-interval [5 15 30 60]
:times -1
}
}
But the data structure is immutable. This is an easy problem in other languages but now I don't know how to do it after a whole afternoon.
So can anyone give me some suggestions? Am I using a bad data structure? Or this problem can be somehow solved in a functional way.
Although you are working with immutable datastructures, you can easily and efficiently return new datastructures that are based on the originals.
In this case, the simplest (if repetitive) solution would be:
(-> m
(update-in [:alert-a :duration] parse-duration)
(update-in [:alert-b :duration] parse-duration)
(update-in [:alert-c :duration] parse-duration)
(update-in [:alert-d :duration] parse-duration))
The important thing to realize here is that update-in does not mutate the datastructure it's working on. Instead it returns a new datastructure with the modifications applied.
The threading macro -> allows the new datastructure to be threaded through the update-in operations, so that the final returned value is the original datastructure with all of the updates applied.
The parse-duration function would probably look a bit like this:
(defn parse-duration
"Convert duration in HH:MM-HH:MM format"
[s]
(let [[t1 t2] (clojure.string/split s #"-"))
(Period. (clj-time.coerce/to-date-time t1)
(clj-time.coerce/to-date-time t2)))
In functional programming you don't modify collection, but instead create new collection with needed values substituted by new ones. Fortunately, Clojure comes with a bunch of useful functions for this. For your case update-in should work well. It takes a collection (e.g. map), sequence of nested keys and a function to apply to the most nested value defined by key sequence. For example:
> (def m {:a 1 :b 2 :c {:c1 1 :c2 2}})
#'sandbox5448/m
> m
{:a 1, :c {:c1 1, :c2 2}, :b 2}
> (update-in m [:c :c1] str)
{:a 1, :c {:c1 "1", :c2 2}, :b 2}
Note how value 1 from key sequence [:c :c1] was converted to "1".
So, converting :duration field of :alert-a to DateTime is as easy as writing:
> (update-in your-map [:alert-a :duration] string-to-date)
where string-to-date is you converter function.