Related
I'm learning Elixir and I'm having a difficulty with this simple problem:
I have a list of values:
my_list = ["a", "b", "c", "y", "z", "a", "e"]
And I have a map:
my_map = %{"a" => -1, "b" => 0, "c" => 1, "d" => 2, "e" => 3}
I want to loop through my_list, find all key occurrences in my_map and sum the values from my_map if the occurrence happened.
In the above example it should return 2 because:
-1 + 0 + 1 + (ignored) + (ignored) - 1 + 3
# => 2
This is a very easy thing to do in languages with mutable variables (we can loop the list and add a counter). I'm working on changing my mindset.
Thank you for help!
I'm working on changing my mindset.
Admirable. You'll find great success in Elixir if you're willing to change your mindset and try to think more functionally. With that in mind, let's break the problem down.
I want to loop through my_list
More precisely, you want to do something to each element of the list and get a list of the results. That's Enum.map/2.
Enum.map(my_list, fn x -> ...)
Now, ... needs to be replaced with what we want to do to each list element. We want to get the corresponding map elements, ignoring those that are not present. Since we're taking a sum, "ignoring" really just means "replacing with zero". Map.get/3 can get a value from a map, using a default if not provided.
Enum.map(my_list, fn x -> Map.get(my_map, x, 0) end)
Now, we have a list of numbers. We just want the sum. That could be done in terms of Enum.reduce/3, but summing is a common enough task that it has its own function: Enum.sum/1.
Enum.sum(Enum.map(my_list, fn x -> Map.get(my_map, x, 0) end))
Finally, this reads backwards. It says "sum the result of mapping over the list", when it would read much cleaner as "take the list, get the elements from the map, then take a sum". We can clean it up with the pipe operator. The following is equivalent to the above.
my_list |> Enum.map(fn x -> Map.get(my_map, x, 0) end) |> Enum.sum
This is a nice use case for a comprehension:
for key <- my_list, val = my_map[key], reduce: 0 do
acc -> acc + val
end
Here the val = my_map[key] is a filter. When key is not in my_map, the result will be nil, which is a falsy value so is skipped.
While both answers given here are perfectly valid, I’m to post another one using plain recursion, for the sake of completeness.
defmodule Summer do
def consume(list, map, acc \\ 0) # head
def consume([], _, acc), do: acc # exhausted
def consume([h | t], map, acc) when is_map_key(map, h),
do: consume(t, map, acc + Map.fetch!(map, h))
def consume([_h | t], map, acc), do: consume(t, map, acc)
end
Summer.consume my_list, my_map
#⇒ 2
I have a pure function that takes 18 arguments process them and returns an answer.
Inside this function I call many other pure functions and those functions call other pure functions within them as deep as 6 levels.
This way of composition is cumbersome to test as the top level functions,in addition to their logic,have to gather parameters for inner functions.
# Minimal conceptual example
main_function(a, b, c, d, e) = begin
x = pure_function_1(a, b, d)
y = pure_function_2(a, c, e, x)
z = pure_function_3(b, c, y, x)
answer = pure_function_4(x,y,z)
return answer
end
# real example
calculate_time_dependant_losses(
Ap,
u,
Ac,
e,
Ic,
Ep,
Ecm_t,
fck,
RH,
T,
cementClass::Char,
ρ_1000,
σ_p_start,
f_pk,
t0,
ts,
t_start,
t_end,
) = begin
μ = σ_p_start / f_pk
fcm = fck + 8
Fr = σ_p_start * Ap
_σ_pb = σ_pb(Fr, Ac, e, Ic)
_ϵ_cs_t_start_t_end = ϵ_cs_ti_tj(ts, t_start, t_end, Ac, u, fck, RH, cementClass)
_ϕ_t0_t_start_t_end = ϕ_t0_ti_tj(RH, fcm, Ac, u, T, cementClass, t0, t_start, t_end)
_Δσ_pr_t_start_t_end =
Δσ_pr(σ_p_start, ρ_1000, t_end, μ) - Δσ_pr(σ_p_start, ρ_1000, t_start, μ)
denominator =
1 +
(1 + 0.8 * _ϕ_t0_t_start_t_end) * (1 + (Ac * e^2) / Ic) * ((Ep * Ap) / (Ecm_t * Ac))
shrinkageLoss = (_ϵ_cs_t_start_t_end * Ep) / denominator
relaxationLoss = (0.8 * _Δσ_pr_t_start_t_end) / denominator
creepLoss = (Ep * _ϕ_t0_t_start_t_end * _σ_pb) / Ecm_t / denominator
return shrinkageLoss + relaxationLoss + creepLoss
end
I see examples of functional composition (dot chaining,pipe operator etc) with single argument functions.
Is it practical to compose the above function using functional programming?If yes, how?
The standard and simple way is to recast your example so that it can be written as
# Minimal conceptual example, re-cast
main_function(a, b, c, d, e) = begin
x = pure_function_1'(a, b, d)()
y = pure_function_2'(a, c, e)(x)
z = pure_function_3'(b, c)(y) // I presume you meant `y` here
answer = pure_function_4(z) // and here, z
return answer
end
Meaning, we use functions that return functions of one argument. Now these functions can be easily composed, using e.g. a forward-composition operator (f >>> g)(x) = g(f(x)) :
# Minimal conceptual example, re-cast, composed
main_function(a, b, c, d, e) = begin
composed_calculation =
pure_function_1'(a, b, d) >>>
pure_function_2'(a, c, e) >>>
pure_function_3'(b, c, y) >>>
pure_function_4
answer = composed_calculation()
return answer
end
If you really need the various x y and z at differing points in time during the composed computation, you can pass them around in a compound, record-like data structure. We can avoid the coupling of this argument handling if we have extensible records:
# Minimal conceptual example, re-cast, composed, args packaged
main_function(a, b, c, d, e) = begin
composed_calculation =
pure_function_1'(a, b, d) >>> put('x') >>>
get('x') >>> pure_function_2'(a, c, e) >>> put('y') >>>
get('x') >>> pure_function_3'(b, c, y) >>> put('z') >>>
get({'x';'y';'z'}) >>> pure_function_4
answer = composed_calculation(empty_initial_state)
return value(answer)
end
The passed around "state" would be comprised of two fields: a value and an extensible record. The functions would accept this state, use the value as their additional input, and leave the record unchanged. get would take the specified field out of the record and put it in the "value" field in the state. put would mutate the extensible record in the state:
put(field_name) = ( {value:v ; record:r} =>
{v ; put_record_field( r, field_name, v)} )
get(field_name) = ( {value:v ; record:r} =>
{get_record_field( r, field_name) ; r} )
pure_function_2'(a, c, e) = ( {value:v ; record:r} =>
{pure_function_2(a, c, e, v); r} )
value(r) = get_record_field( r, value)
empty_initial_state = { novalue ; empty_record }
All in pseudocode.
Augmented function application, and hence composition, is one way of thinking about "what monads are". Passing around the pairing of a produced/expected argument and a state is known as State Monad. The coder focuses on dealing with the values while treating the state as if "hidden" "under wraps", as we do here through the get/put etc. facilities. Under this illusion/abstraction, we do get to "simply" compose our functions.
I can make a small start at the end:
sum $ map (/ denominator)
[ _ϵ_cs_t_start_t_end * Ep
, 0.8 * _Δσ_pr_t_start_t_end
, (Ep * _ϕ_t0_t_start_t_end * _σ_pb) / Ecm_t
]
As mentioned in the comments (repeatedly), the function composition operator does indeed accept multiple argument functions. Cite: https://docs.julialang.org/en/v1/base/base/#Base.:%E2%88%98
help?> ∘
"∘" can be typed by \circ<tab>
search: ∘
f ∘ g
Compose functions: i.e. (f ∘ g)(args...; kwargs...) means f(g(args...; kwargs...)). The ∘ symbol
can be entered in the Julia REPL (and most editors, appropriately configured) by typing
\circ<tab>.
Function composition also works in prefix form: ∘(f, g) is the same as f ∘ g. The prefix form
supports composition of multiple functions: ∘(f, g, h) = f ∘ g ∘ h and splatting ∘(fs...) for
composing an iterable collection of functions.
The challenge is chaining the operations together, because any function can only pass on a tuple to the next function in the composed chain. The solution could be making sure your chained functions 'splat' the input tuples into the next function.
Example:
# splat to turn max into a tuple-accepting function
julia> f = (x->max(x...)) ∘ minmax;
julia> f(3,5)
5
Using this will in no way help make your function cleaner, though, in fact it will probably make a horrible mess.
Your problems do not at all seem to me to be related to how you call, chain or compose your functions, but are entirely due to not organizing the inputs in reasonable types with clean interfaces.
Edit: Here's a custom composition operator that splats arguments, to avoid the tuple output issue, though I don't see how it can help picking the right arguments, it just passes everything on:
⊕(f, g) = (args...) -> f(g(args...)...)
⊕(f, g, h...) = ⊕(f, ⊕(g, h...))
Example:
julia> myrev(x...) = reverse(x);
julia> (myrev ⊕ minmax)(5,7)
(7, 5)
julia> (minmax ⊕ myrev ⊕ minmax)(5,7)
(5, 7)
My julia version is 1.7.1.
These code can recurrent my problem:
struct Foo1
sixsix
aa
bb
disposion
end
struct Foo2
aa
bb
end
function Base.:+(f1::Foo1, f2 :: Foo2)
newf = f1
for n in fieldnames(typeof(f2))
getproperty(newf, n) += getproperty(f2, n)
end
return newf
end
returned LoadError: syntax: invalid assignment location "getproperty(newf, n)"
same LoadError happened when I try to use getfield:
function Base.:+(f1::Foo1, f2 :: Foo2)
newf = f1
for n in fieldnames(typeof(f2))
getfield(newf, n) += getfield(f2, n)
end
return newf
end
Firstly, you must make your structs mutable for this to work.
Secondly, this:
getproperty(newf, n) += getproperty(f2, n)
is expanded into
getproperty(newf, n) = getproperty(newf, n) + getproperty(f2, n)
In other words you are apparently trying to assign a value into a function call. In Julia you can only assign to variables, not to values. The only thing this syntax is allowed for is function definition. From the manual:
There is a second, more terse syntax for defining a function in Julia.
The traditional function declaration syntax demonstrated above is
equivalent to the following compact "assignment form":
julia> f(x,y) = x + y
f (generic function with 1 method)
So the syntax you are using doesn't do what you want, and what you want to do is not possible anyway, since you can only assign to variables, not to values.
What you are trying to do can be achieved like this (assuming n is a Symbol):
setfield!(newf, n, getfield(newf, n) + getfield(f2, n))
I would rather recommend you to do the following:
Base.:+(f1::Foo1, f2 :: Foo2) =
Foo1(f1.sixsix, f1.aa+f2.aa, f1.bb+f2.bb, f1.disposion)
Your code is wrong for the following reasons:
Foo1 is not mutable, so you cannot change values of its elements
to set a field of mutable struct (which your struct is not) you use setfield!
You mix properties and fields of a struct which are not the same.
fieldnames works on types not on instances of type.
If you wanted to be more fancy and do automatic detection of matching fields you could do:
Base.:+(f1::Foo1, f2 :: Foo2) =
Foo1((n in fieldnames(Foo2) ?
getfield(f1, n) + getfield(f2, n) :
getfield(f1, n) for n in fieldnames(Foo1))...)
However, I would not recommend it, as I feel that it is overly complicated.
I am trying to do in Julia what this Python code does. (Find all pairs from the two lists whose combined value is above 7.)
#Python
def sum_is_large(a, b):
return a + b > 7
l1 = [1,2,3]
l2 = [4,5,6]
l3 = [(a,b) for a in l1 for b in l2 if sum_is_large(a, b)]
print(l3)
There is no if for list comprehensions in Julia. And if I use filter(), I'm not sure if I can pass two arguments. So my best suggestion is this:
#Julia
function sum_is_large(pair)
a, b = pair
return a + b > 7
end
l1 = [1,2,3]
l2 = [4,5,6]
l3 = filter(sum_is_large, [(i,j) for i in l1, j in l2])
print(l3)
I don't find this very appealing. So my question is, is there a better way in Julia?
Using the very popular package Iterators.jl, in Julia:
using Iterators # install using Pkg.add("Iterators")
filter(x->sum(x)>7,product(l1,l2))
is an iterator producing the pairs. So to get the same printout as the OP:
l3iter = filter(x->sum(x)>7,product(l1,l2))
for p in l3iter println(p); end
The iterator approach is potentially much more memory efficient. Ofcourse, one could just l3 = collect(l3iter) to get the pair vector.
#user2317519, just curious, is there an equivalent iterator form for python?
Guards (if) are now available in Julia v0.5 (currently in the release-candidate stage):
julia> v1 = [1, 2, 3];
julia> v2 = [4, 5, 6];
julia> v3 = [(a, b) for a in v1, b in v2 if a+b > 7]
3-element Array{Tuple{Int64,Int64},1}:
(3,5)
(2,6)
(3,6)
Note that generators are also now available:
julia> g = ( (a, b) for a in v1, b in v2 if a+b > 7 )
Base.Generator{Filter{##18#20,Base.Prod2{Array{Int64,1},Array{Int64,1}}},##17#19}(#17,Filter{##18#20,Base.Prod2{Array{Int64,1},Array{Int64,1}}}(#18,Base.Prod2{Array{Int64,1},Array{Int64,1}}([1,2,3],[4,5,6])))
Another option similar to the one of #DanGetz using also Iterators.jl:
function expensive_fun(a, b)
return (a + b)
end
Then, if the condition is also complicated, it can be defined as a function:
condition(x) = x > 7
And last, filter the results:
>>> using Iterators
>>> result = filter(condition, imap(expensive_fun, l1, l2))
result is an iterable that is only computed when needed (inexpensive) and can be collected collect(result) if required.
The one-line if the filter condition is simple enough would be:
>>> result = filter(x->(x > 7), imap(expensive_fun, l1, l2))
Note: imap works natively for arbitrary number of parameters.
Perhaps something like this:
julia> filter(pair -> pair[1] + pair[2] > 7, [(i, j) for i in l1, j in l2])
3-element Array{Tuple{Any,Any},1}:
(3,5)
(2,6)
(3,6)
although I'd agree it doesn't look like it ought to be the best way...
I'm surprised nobody mentions the ternary operator to implement the conditional:
julia> l3 = [sum_is_large((i,j)) ? (i,j) : nothing for i in l1, j in l2]
3x3 Array{Tuple,2}:
nothing nothing nothing
nothing nothing (2,6)
nothing (3,5) (3,6)
or even just a normal if block within a compound statement, i.e.
[ (if sum_is_large((x,y)); (x,y); end) for x in l1, y in l2 ]
which gives the same result.
I feel this result makes a lot more sense than filter(), because in julia the a in A, b in B construct is interpreted dimensionally, and therefore the output is in fact an "array comprehension" with appropriate dimensionality, which clearly in many cases would be advantageous and presumably the desired behaviour (whether we include a conditional or not).
Whereas filter will always return a vector. Obviously, if you really want a vector result you can always collect the result; or for a conditional list comprehension like the one here, you can simply remove nothing elements from the array by doing l3 = l3[l3 .!= nothing].
Presumably this is still clearer and no less efficient than the filter() approach.
You can use the #vcomp (vector comprehension) macro in VectorizedRoutines.jl to do Python-like comprehensions:
using VectorizedRoutines
Python.#vcomp Int[i^2 for i in 1:10] when i % 2 == 0 # Int[4, 16, 36, 64, 100]
How to generate even numbers in this way using recursive functions in Erlang.
Note: The length of the output list is the input of the function func
Example:
> mod:func(5).
[2,4,6,8,10]
Many ways to do that in erlang, I suggest:
with sequence generators
doubles(Number)->
lists:seq(2,Number*2,2).
with list comprehensions
doubles(Number)->
[X*2 || X <- lists:seq(1,Number)].
the recursive way
doubles(Max)->
doubles(1,Max).
doubles(Max,Max)->
[Max*2];
doubles(Val,Max)->
[Val*2]++doubles2(Val+1,Max).
Here's an example module:
-module(even_numbers).
-export([get_first_n/1]).
get_first_n(Count) ->
get_first_n(Count, 2).
get_first_n(1, Current) ->
[Current];
get_first_n(Count, Current) ->
[Current] ++ f(Count - 1, Current + 2).
There is one simple and natural Erlangish way:
func(N) when is_integer(N) ->
func(2, N).
func(X, N) when N > 0 ->
[X | func(X+2, N-1)];
func(_, _) -> [].
For me tail-recursive way is more natural Erlang way:
func(N) when is_integer(N) andalso (N >= 0) ->
func(N, 1, []).
func(0, _, Acc) ->
lists:reverse(Acc);
func(N, I, Acc) ->
func(N - 1, I + 1, [I*2|Acc]).