Unroll nesting in recursive map in clojure - recursion

I'm having trouble unrolling the nested structure of the results of this function. I think it's performing the traversal correctly but the results are deeply nested.
The function attempts to traverse the list and find all of the possible paths through the list where the numerical distance between adjacent elements is either 1, 2, or 3. For example, to go from the first element 1, to the next element 4 only a step of 3 will work. But, for the next step we can either take 1 from 4 to 5, we can take two from 4 to 6 (skipping 5), or three from 4 to 7 (skipping 5 and 6).
My question is two fold: 1) what can I do to prevent the deep nesting? and 2) is there a different way to structure the code to prevent the deep nesting in the first place?
(def l0 (1 4 5 6 7 10 11 12 15 16 19 22))
(defn next-path-with-step [chain step]
(let [looking-at (first chain)
next-path (drop-while (fn [a]
(not (= (- a looking-at) step)))
chain)]
(if (empty? next-path)
nil
next-path)))
(defn find-chains [chains prefix]
(if (empty? chains)
prefix
(map (fn [chain]
(let [head (first chain)
next-paths (filter #(not (nil? %))
(map (partial next-path-with-step chain) [1 2 3]))]
(find-chains next-paths (conj prefix head))))
chains)))
To run it:
(find-chains [l0] [])
I get the following results:
(((((((((((([1 4 5 6 7 10 11 12 15 16 19 22]))))) (((([1 4 5 6 7 10 12 15 16 19 22]))))))) ((((((([1 4 5 7 10 11 12 15 16 19 22]))))) (((([1 4 5 7 10 12 15 16 19 22]))))))) (((((((([1 4 6 7 10 11 12 15 16 19 22]))))) (((([1 4 6 7 10 12 15 16 19 22]))))))) ((((((([1 4 7 10 11 12 15 16 19 22]))))) (((([1 4 7 10 12 15 16 19 22])))))))))
What I'm trying to get are the inner sequences as a list. Something like:
([1 4 5 6 7 10 11 12 15 16 19 22]
[1 4 5 6 7 10 12 15 16 19 22]
[1 4 5 7 10 11 12 15 16 19 22]
[1 4 5 7 10 12 15 16 19 22]
[1 4 6 7 10 11 12 15 16 19 22]
[1 4 6 7 10 12 15 16 19 22]
[1 4 7 10 11 12 15 16 19 22]
[1 4 7 10 12 15 16 19 22])

here are the minor changes, that will solve that for you:
(defn find-chains [chains prefix]
(if (empty? chains)
[prefix] ;; return chain wrapped in a vector to avoid flattening
;; use mapcat instead of map to flatten internal find-chains calls
(mapcat (fn [chain]
(let [head (first chain)
next-paths (filter #(not (nil? %))
(map (partial next-path-with-step chain) [1 2 3]))]
(find-chains next-paths (conj prefix head))))
chains)))
user> (find-chains [l0] [])
;;=> ([1 4 5 6 7 10 11 12 15 16 19 22]
;; [1 4 5 6 7 10 12 15 16 19 22]
;; [1 4 5 7 10 11 12 15 16 19 22]
;; [1 4 5 7 10 12 15 16 19 22]
;; [1 4 6 7 10 11 12 15 16 19 22]
;; [1 4 6 7 10 12 15 16 19 22]
;; [1 4 7 10 11 12 15 16 19 22]
;; [1 4 7 10 12 15 16 19 22])

Related

R Script to rearrange the elements of a vector by interleaving it

How to write an R-script to initialize a vector with integers, rearrange the elements by interleaving the
first half elements with the second half elements and store in the same vector without using pre-defined function and display the updated vector.
This sounds like a homework question, and it would be nice to see some effort on your own part, but it's pretty straightforward to do this in R.
Suppose your vector looks like this:
vec <- 1:20
vec
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Then you can just do:
c(t(cbind(vec[1:10], vec[11:20])))
#> [1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20
This works by joining the two vectors into a 10 x 2 matrix, then transposing that matrix and turning it into a vector.
We may use matrix directly and concatenate
c(matrix(vec, nrow = 2, byrow = TRUE))
-output
[1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20
data
vec <- 1:20
Or using mapply:
vec <- 1:20
c(mapply(\(x,y) c(x,y), vec[1:10], vec[11:20]))
#> [1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20
We can try this using order + %%
> vec[order((seq_along(vec) - 1) %% (length(vec) / 2))]
[1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20
Another way is to use rbind on the 2 halves of the vector, which creates a matrix with two rows. Then, we can then turn the matrix into a vector, which will go through column by column (i.e., 1, 11, 2, 12...). However, this will only work for even vectors.
vec <- 1:20
c(rbind(vec[1:10], vec[11:20]))
# [1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20
So, for uneven vectors, we can use order, which will return the indices of the numbers in the two seq_along vectors.
vec2 <- 1:21
order(c(seq_along(vec2[1:10]),seq_along(vec2[11:21])))
# [1] 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9 19 10 20 21

`map` equivalent to 2d list comprehension

In 1d, I can use either of these:
[i for i in 1:5]
or
map(1:5) do i
i
end
both produce
[1,2,3,4,5]
Is there a way to use map in higher dimensions? e.g. to replicate
[x + y for x in 1:5,y in 10:13]
which produces
5×4 Array{Int64,2}:
11 12 13 14
12 13 14 15
13 14 15 16
14 15 16 17
15 16 17 18
You can do this:
julia> map(Iterators.product(1:3, 10:15)) do (x,y)
x+y
end
3×6 Array{Int64,2}:
11 12 13 14 15 16
12 13 14 15 16 17
13 14 15 16 17 18
The comprehension you wrote is I think just collect(x+y for (x,y) in Iterators.product(1:5, 10:13)), . Note the brackets (x,y), as the do function gets a tuple. Unlike x,y when it gets two arguments:
julia> map(1:3, 11:13) do x,y
x+y
end
3-element Array{Int64,1}:
12
14
16
This, of course, is not the map equivalent that you are looking for, but in some cases like this you can use broadcasting with a vector and a transposed vector:
x = 1:5
y = (10:13)'
x .+ y
At the REPL:
julia> (1:5) .+ (10:13)'
5×4 Array{Int64,2}:
11 12 13 14
12 13 14 15
13 14 15 16
14 15 16 17
15 16 17 18

Sum a variable based on another variable

I have a dataset consisting of two variables, Contents and Time like so:
Time Contents
2017M01 123
2017M02 456
2017M03 789
. .
. .
. .
2018M12 789
Now I want to create a numeric vector that aggregates Contents for six months, that is I want to sum 2017M01 to 2017M06 to one number, 2017M07 to 2017M12 to another number and so on.
I'm able to do this by indexing but I want to be able to write: "From 2017M01 to 2017M06 sum contents corresponding to that sequence" in my code.
I would really appreciate some help!
You can create a grouping variable based on the number of rows and number of elements to group. For your case, you want to group every 6 rows so your data frame should be divisible with 6. Using iris to demonstrate (It has 150 rows, so 150 / 6 = 25)
rep(seq(nrow(iris)%/%6), each = 6)
#[1] 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 8 8 8 8 8 8 9 9 9 9 9 9 10 10 10 10
#[59] 10 10 11 11 11 11 11 11 12 12 12 12 12 12 13 13 13 13 13 13 14 14 14 14 14 14 15 15 15 15 15 15 16 16 16 16 16 16 17 17 17 17 17 17 18 18 18 18 18 18 19 19 19 19 19 19 20 20
#[117] 20 20 20 20 21 21 21 21 21 21 22 22 22 22 22 22 23 23 23 23 23 23 24 24 24 24 24 24 25 25 25 25 25 25
There are plenty of ways to handle how you want to call it. Here is a custom function that allows you to do that (i.e. create the grouping variable),
f1 <- function(x, df) {
v1 <- as.numeric(gsub('[0-9]{4}M(.*):[0-9]{4}M(.*)$', '\\1', x))
v2 <- as.numeric(gsub('[0-9]{4}M(.*):[0-9]{4}M(.*)$', '\\2', x))
i1 <- (v2 - v1) + 1
return(rep(seq(nrow(df)%/%i1), each = i1))
}
f1("2017M01:2017M06", iris)
#[1] 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 8 8 8 8 8 8 9 9 9 9 9 9 10 10 10 10
#[59] 10 10 11 11 11 11 11 11 12 12 12 12 12 12 13 13 13 13 13 13 14 14 14 14 14 14 15 15 15 15 15 15 16 16 16 16 16 16 17 17 17 17 17 17 18 18 18 18 18 18 19 19 19 19 19 19 20 20
#[117] 20 20 20 20 21 21 21 21 21 21 22 22 22 22 22 22 23 23 23 23 23 23 24 24 24 24 24 24 25 25 25 25 25 25
EDIT: We can easily make the function compatible with 'non-0-remainder' divisions by concatenating the final result with a repetition of the max+1 value of the final result of remainder times, i.e.
f1 <- function(x, df) {
v1 <- as.numeric(gsub('[0-9]{4}M(.*):[0-9]{4}M(.*)$', '\\1', x))
v2 <- as.numeric(gsub('[0-9]{4}M(.*):[0-9]{4}M(.*)$', '\\2', x))
i1 <- (v2 - v1) + 1
final_v <- rep(seq(nrow(df) %/% i1), each = i1)
if (nrow(df) %% i1 == 0) {
return(final_v)
} else {
remainder = nrow(df) %% i1
final_v1 <- c(final_v, rep((max(final_v) + 1), remainder))
return(final_v1)
}
}
So for a data frame with 20 rows, doing groups of 6, the above function will yield the result:
f1("2017M01:2017M06", df)
#[1] 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 4 4

Concatencation+collecting of ranges in Julia

According to documentation, [A; B; C; ...] calls vcat(). So, to concatenate and collect a comprehension of ranges this way
>>[1:4; 6:9; 20:23]
12-element Array{Int64,1}:
1
2
3
4
6
7
8
9
20
21
22
23
I tried to use vcat(), but it does not do collecting
vcat([i:i+3 for i in [1,6,20]])
3-element Array{UnitRange{Int64},1}:
1:4
6:9
20:23
Is there a simple way to collect all values from a comprehension of ranges?
Simply add ... to get
julia> vcat([i:i+3 for i in [1,6,20]]...)
12-element Array{Int64,1}:
1
2
3
4
6
7
8
9
20
21
22
23

Alphabet conversion - Cyrillic to Latin

I have a list of names and surnames written on Cyrillic.
head(text, n = 20)
unique(clients$RODITEL)
1 <NA>
2 ЃОРЃИ
3 ALEKSANDAR
4 000000000000
5 ТР4АЈЧЕ
6 0
7 HHHHHHH
8 0000000
9 TASKO
10 --------------------
11 ДРАГИ
12 СЛАВЧО
13 ACO
14 НИКОЛА
15 САШО
16 НАУМЧЕ
17 ОРЦЕ
18 САНДРА
19 МИРСАД
20 ОКТАЈ
What I need to do is to convert the names written on Cyrlic, such as the last 10 rows into Latin.
So the output would be:
1 <NA>
2 GJORGJI
3 ALEKSANDAR
4 000000000000
5 TRAJCHE
6 0
7 HHHHHHH
8 0000000
9 TASKO
10 --------------------
11 DRAGI
12 SLAVCHO
13 ACO
14 NIKOLA
15 SASHO
16 NAUMCHE
17 ORCE
18 SANDRA
19 MIRSAD
20 OKTAJ
The particular, Cyrlic alphabet is Macedonian.
I am not sure if there is any R package that deals with such conversion?
You can use functions from the package stringi, for example:
> stri_trans_general('ДРАГИ', 'latin')
[1] "DRAGI"

Resources