I have this map
%{
total: 38,
value: 22
}
And would like to add the key :ratio. Is there a way to write immediately:
%{
total: 38,
value: 22,
ratio: __SELF__.value / __SELF__.total
}
or do I need to create another map to achieve this?
Thanks
All data is immutable, so you always have to make a new map.
A simple way, assuming your map is called map:
iex> Map.put(map, :ratio, map.value / map.total)
%{ratio: 0.5789473684210527, total: 38, value: 22}
If you mean that you want to create the map before it already exists, then it would be better to put total and value into variables, and use them to build the map:
defmodule Example do
def make_map(total, value) do
%{total: total, value: value, ratio: value / total}
end
end
iex(1)> Example.make_map(38, 22)
%{ratio: 0.5789473684210527, total: 38, value: 22}
I really wanted to avoid assigning the temporary map to a variable.
Well, because the data at a specific memory location is immutable, elixir can safely use pointers to those memory locations inside new collections. Therefore, creating a temporary variable with intermediate results does not double the amount of memory used. For instance, if you begin with a map that has 1 million entries and you create a new map with one additional entry, you do not use total memory of:
old_map new_map
| |
V V
1 million + ( 1 million + 1)
Rather you only use additional memory of:
new_entry
|
V
1 + pointer_to_old_map
...plus a little extra for the new map's bookkeeping. Like this:
old_map
^
|
new_map = %{ +, a: 10}
Is there a way to write immediately:
%{
total: 38,
value: 22,
ratio: __SELF__.value / __SELF__.total
}
Yes:
%{
total: 38,
value: 22,
ratio: 22/38
}
Now, if you have a list of maps to which you want to add a ratio key:
data = [
%{total: 38, value: 22},
%{total: 40, value: 22},
%{total: 44, value: 22}
]
for %{total: t, value: v}=map <- data do
Map.put(map, :ratio, v/t)
end
output:
[
%{ratio: 0.5789473684210527, total: 38, value: 22},
%{ratio: 0.55, total: 40, value: 22},
%{ratio: 0.5, total: 44, value: 22}
]
After each iteration of the for comprehension, the memory locations of t and v are immediately subject to garbage collection and a pointer to the memory location of map is used in the new map.
Related
Does openscad have any language primitive for string-keyed associative arrays (a.k.a hash maps, a.k.a dictionaries)? Or is there any convention for how to emulate associative arrays?
So far all I can think of is using vectors and using variables to map indexes into the vector to human readable names. That means there's no nice, readable way to define the vector, you just have to comment it.
Imagine I want to write something akin to the Python data structure:
bobbin_metrics = {
'majacraft': {
'shaft_inner_diameter': 9.0,
'shaft_outer_diameter': 19.5,
'close_wheel_diameter': 60.1,
# ...
},
'majacraft_jumbo': {
'shaft_inner_diameter': 9.0,
'shaft_outer_diameter': 25.0,
'close_wheel_diameter': 100.0,
},
# ...
}
such that I can reference it in model definitions in some recognisably hash-map-like way, like passing bobbin_metrics['majacraft'] to something as metrics and referencing metrics['close_wheel_diameter'].
So far my best effort looks like
# Vector indexes into bobbin-metrics arrays
BM_SHAFT_INNER_DIAMETER = 0
BM_SHAFT_OUTER_DIAMETER = 1
BM_CLOSE_WHEEL_DIAMETER = 2
bobbin_metrics_majacraft = [
9.0, # shaft inner diameter
19.5, # shaft outer diameter
60.1, # close-side wheel diameter
# ....
];
bobbin_metrics_majacraft_jumbo = [
9.0, # shaft inner diameter
25.0, # shaft outer diameter
100.0, # close-side wheel diameter
# ....
];
bobbin_metrics = [
bobbin_metrics_majacraft,
bobbin_metrics_majacraft_jumbo,
# ...
];
# Usage when passed a bobbin metrics vector like
# bobbin_metrics_majacraft as 'metrics' to a function
metrics[BM_SHAFT_INNER_DIAMETER]
I think that'll work. But it's U.G.L.Y.. Not quite "I write applications in bash" ugly, but not far off.
Is there a better way?
I'm prepared to maintain the data set outside openscad and have a generator for an include file if I have to, but I'd rather not.
Also, in honour of April 1 I miss the blink tag and wonder if the scrolling marquee will work? Tried 'em :)
I played around with the OpenSCAD search() function which is documented in the manual here;
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#Search
The following pattern allows a form of associative list, it may not be optimal but does provide a way to set up a dictionary structure and retrieve a value against a string key;
// associative searching
// dp 2019
// - define the dictionary
dict = [
["shaft_inner_diameter", 9.0],
["shaft_outer_diameter", 19.5],
["close_wheel_diameter", 60.1]
];
// specify the serach term
term = "close_wheel_diameter";
// execute the search
find = search(term, dict);
// process results
echo("1", find);
echo ("2",dict[find[0]]);
echo ("3",dict[find[0]][1]);
The above produces;
Compiling design (CSG Tree generation)...
WARNING: search term not found: "l"
...
WARNING: search term not found: "r"
ECHO: "1", [2, 0]
ECHO: "2", ["close_wheel_diameter", 60.1]
ECHO: "3", 60.1
Personally, I would do this sort of thing in Python then generate the OpenSCAD as an intermediate file or maybe use the SolidPython library.
An example of a function that uses search() and does not produce any warnings.
available_specs = [
["mgn7c", 1,2,3,4],
["mgn7h", 2,3,4,5],
];
function selector(item) = available_specs[search([item], available_specs)[0]];
chosen_spec = selector("mgn7c");
echo("Specification was returned from function", chosen_spec);
The above will produce the following output:
ECHO: "Specification was returned from function", ["mgn7c", 1, 2, 3, 4]
Another very similar approach is using list comprehensions with a condition statement, just like you would in Python for example. Does the same thing, looks a bit simpler.
function selector(item) = [
for (spec = available_specs)
if (spec[0] == item)
spec
];
My coin change dynamic programming implementation is failing for some of the test cases, and I am having a hard time figuring out why:
Problem Statement: Given an amount and a list of coins, find the minimum number of coins required to make that amount.
Ex:
Target Amount: 63
Coin List: [1, 5, 10, 21, 25]
Output: [21, 21, 21]
def coin_change(change_list, amount, tried):
if amount <= 0:
return []
if amount in change_list:
return [amount]
if amount in tried:
return tried[amount]
coin_count = []
for change in change_list:
if change < amount:
changes = coin_change(change_list, amount-change, tried)
changes.append(change)
coin_count.append(changes)
min_changes = coin_count[0][:]
for x in coin_count[1:]:
if len(min_changes) >= len(x):
min_changes = x[:]
tried[amount] = min_changes[:]
return min_changes
def main():
for amount in range(64):
changes = coin_change([1, 5, 10, 21, 25], amount, {})
if sum(changes) != amount:
print "WRONG: Change for %d is: %r" % (amount, changes)
else:
# print "Change for %d is: %r" % (amount, changes)
pass
if __name__ == "__main__":
main()
Trinket: https://trinket.io/python/43fcff035e
You're corrupting the variable, changes, by appending to it during a loop. Try this:
Replace these two lines:
changes.append(change)
coin_count.append(changes)
With:
_changes = changes[:] + [change]
coin_count.append(_changes)
I'm a beginner in Elixir programming language.
I have an object something like this:
{
id: uuid,
created_at: DateTime.t,
updated_at: DateTime.t,
type: my_type
}
let's say my_type is one of ~w[A B C D]
I want to write a function which takes a list of these objects and returns a following map:
%{
A: 120,
B: 220,
C: 560,
D: 0,
any: 560
}
The values here has to be the MAXIMUM difference between updated_at and created_at columns(Timex.diff(updated_at, created_at, :seconds)) per my_type + any in addition.
In case of any the my_type is not considered and takes the maximum among all objects in the list.
What is the best way to do it in Elixir? Thanks.
The following will group the list by its type, then calculate the max difference for each group, and finally result in a map containing each type as the key and max difference as the value.
map = list
|> Enum.group_by(& &1.type)
|> Enum.map(fn {type, values} ->
max =
values
|> Enum.map(fn %{created_at: created_at, updated_at: updated_at} ->
# return the difference here
end)
|> Enum.max
{type, max}
end)
|> Map.new
This should give you something like:
%{
A: 120,
B: 220,
C: 560,
D: 0
}
You can calculate the value for any now by doing map |> Map.values |> Enum.max.
I am posting this here for the sake of formatting and diversity. The answer by #Dogbert is likely better fit, although this approach could be more straightforward and arguably flexible.
list = [%{type: :a, v1: 10, v2: 20},
%{type: :a, v1: 10, v2: 30},
%{type: :b, v1: 10, v2: 20},
%{type: :c, v1: 10, v2: 20}]
kw = for %{type: t, v1: v1, v2: v2} <- list,
do: {t, v2 - v1}, into: []
#⇒ [a: 10, a: 20, b: 10, c: 10]
kw
|> Enum.sort_by(fn {_, v} -> v end, &>=/2)
|> Enum.uniq_by(fn {k, _} -> k end)
#⇒ [a: 20, b: 10, c: 10]
I wrote a max function which takes a Vec as a parameter. It works as I expected. Then I added a min function the same as the max function:
fn main() {
let my_array = vec![61, 14, 71, 23, 42, 8, 13, 66];
let max = max(my_array);
let min = min(my_array);
println!("Max value is {}.", max);
}
fn max(array: Vec<i32>) -> i32 {
let mut max = array[0];
for val in array {
if max < val {
max = val;
}
}
max
}
fn min(array: Vec<i32>) -> i32 {
let mut min = array[0];
for val in array {
if min > val {
min = val;
}
}
min
}
Rust reports an error if I put the same my_array parameter on the call to min:
error[E0382]: use of moved value: `my_array`
--> src/main.rs:4:19
|
2 | let my_array = vec![61, 14, 71, 23, 42, 8, 13, 66];
| -------- move occurs because `my_array` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
3 | let max = max(my_array);
| -------- value moved here
4 | let min = min(my_array);
| ^^^^^^^^ value used here after move
How can I write code that works?
This is an issue that very beginners to Rust will experience. As a beginner, you should read The Rust Programming Language. A lot of effort has been put into this book, especially for newcomers to Rust. This will cover many things that you will run into.
Relevant sections:
Data Types
Understanding Ownership
References and borrowing
The underlying problem is that you've transferred ownership of the vector when you call max. The value is then gone; main no longer has it.
The simplest thing to to is to clone the vector before passing to max. This allows main to keep ownership of my_array and then transfer ownership to min on the subsequent line:
let max = max(my_array.clone());
let min = min(my_array);
This is inefficient, as neither max nor min need to take ownership of the vector to do their work. Cloning the Vec also requires additional memory allocation. It's more idiomatic to pass in a slice, which is a type of reference to the data inside the Vec:
let max = max(&my_array);
let min = min(&my_array);
fn max(array: &[i32]) -> i32 {
let mut max = array[0];
for &val in array {
if max < val {
max = val;
}
}
max
}
When iterating over a slice, you get back references to the items in the slice. With integers, we can dereference them (here using the & in for &val in array) and make a copy of the value.
See also:
Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?
Even better, there's no need to rewrite basic functions like this. You also assume there's always at least one value, which isn't true for an empty vector. The idiomatic solution is to use iterators:
fn main() {
let my_array = vec![61, 14, 71, 23, 42, 8, 13, 66];
let max = my_array.iter().max();
let min = my_array.iter().min();
println!("Max value is {:?}.", max);
println!("Min value is {:?}.", min);
}
This uses Iterator::min and Iterator::max,
which each return an Option, as an empty slice has no minimum or maximum value.
Technically, it's a little different from your original solution, as min and max are Option<&i32>; a reference to the original slice. You can get back to Option<i32> by using Option::copied:
fn main() {
let my_array = vec![61, 14, 71, 23, 42, 8, 13, 66];
let max = my_array.iter().max().copied();
let min = my_array.iter().min().copied();
println!("Max value is {:?}.", max);
println!("Min value is {:?}.", min);
}
Bonus information: slices, Vecs, and arrays are all different types. It's not correct to refer to my_array as an array at all.
The following go code doesn't compile, because (I believe) there is a mistake around the way pointers are being referenced.
In particular, The error message is
prog.go:13: cannot use append((*x)[:remove], (*x)[remove + 1:]...) (type []int) as type *[]int in assignment
Here is an abstracted and simplified version of the code which results in this error message.
package main
import "fmt"
func main() {
x := &[]int{11, 22, 33, 44, 55, 66, 77, 88, 99}
for i, addr := range *x {
if addr == 22 {
for len(*x) > 5 {
remove := (i + 1) % len(*x)
x = append((*x)[:remove], (*x)[remove+1:]...)
}
break
}
}
fmt.Println(x)
}
You're not using an array here, you're using a slice. Generally, you don't want to handle a pointer to a slice since it can get awkward, and the pointer is needed in very few cases.
To fix your error, dereference x:
*x = append((*x)[:remove], (*x)[remove+1:]...)
But you should probably be using the slice value directly, so that no dereferences are required:
x := []int{11, 22, 33, 44, 55, 66, 77, 88, 99}