How do "variables" work with recursion in Elixir? - variables

I am an absolute beginner in Elixir and I tried toying around with recursion by making a function like this:
def player_choice() do
choice = IO.gets("Choose rock, paper or scissors: ") |> String.trim()
unless String.downcase(choice) in ["rock", "paper", "scissors"] do
IO.puts("\nPlease insert a valid move")
choice = player_choice()
end
choice
end
So if someone inputs a value thats not expected the function should continue asking for an input.
But if the first input is something unexpected like "no", and the next input is "rock" the function will return "no". Why does this happen, shouldn't "choice" be the reassigned value?

The problem is that you redefine a new variable choice in your internal unless, masking the outer one (in Elixir, you don't "reassign"). The compiler warns you:
variable "choice" is unused (there is a variable with the same name in
the context
A better solution would be something like:
def player_choice() do
choice = IO.gets("Choose rock, paper or scissors: ") |> String.trim() |> String.downcase
if choice in ["rock", "paper", "scissors"] do
choice
else
IO.puts("\nPlease insert a valid move")
player_choice()
end
end

Pattern matching seems an idiomatic way to approach this
defmodule Playground do
def player_choice(choice) when choice in ["rock", "paper", "scissors"], do: choice
def player_choice(_) do
IO.puts("\nPlease insert a valid move")
prompt()
end
def prompt() do
IO.gets("Choose rock, paper or scissors: ")
|> String.trim
|> String.downcase
|> Playground.player_choice
end
end

Related

Compact way to save JuMP optimization results in DataFrames

I would like to save all my variables and dual variables of my finished lp-optimization in an efficient manner. My current solution works, but is neither elegant nor suited for larger optimization programs with many variables and constraints because I define and push! every single variable into DataFrames separately. Is there a way to iterate through the variables using all_variables() and all_constraints() for the duals? While iterating, I would like to push the results into DataFrames with the variable index name as columns and save the DataFrame in a Dict().
A conceptual example would be for variables:
Result_vars = Dict()
for vari in all_variables(Model)
Resul_vars["vari"] = DataFrame(data=[indexval(vari),value(vari)],columns=[index(vari),"Value"])
end
An example of the appearance of the declared variable in JuMP and DataFrame:
#variable(Model, p[t=s_time,n=s_n,m=s_m], lower_bound=0,base_name="Expected production")
And Result_vars[p] shall approximately look like:
t,n,m,Value
1,1,1,50
2,1,1,60
3,1,1,145
Presumably, you could go something like:
x = all_variables(model)
DataFrame(
name = variable_name.(x),
Value = value.(x),
)
If you want some structure more complicated, you need to write custom code.
T, N, M, primal_solution = [], [], [], []
for t in s_time, n in s_n, m in s_m
push!(T, t)
push!(N, n)
push!(M, m)
push!(primal_solution, value(p[t, n, m]))
end
DataFrame(t = T, n = N, m = M, Value = primal_solution)
See here for constraints: https://jump.dev/JuMP.jl/stable/constraints/#Accessing-constraints-from-a-model-1. You want something like:
for (F, S) in list_of_constraint_types(model)
for con in all_constraints(model, F, S)
#show dual(con)
end
end
Thanks to Oscar, I have built a solution that could help to automatize the extraction of results.
The solution is build around a naming convention using base_name in the variable definition. One can copy paste the variable definition into base_name followed by :. E.g.:
#variable(Model, p[t=s_time,n=s_n,m=s_m], lower_bound=0,base_name="p[t=s_time,n=s_n,m=s_m]:")
The naming convention and syntax can be changed, comments can e.g. be added, or one can just not define a base_name. The following function divides the base_name into variable name, sets (if needed) and index:
function var_info(vars::VariableRef)
split_conv = [":","]","[",","]
x_str = name(vars)
if occursin(":",x_str)
x_str = replace(x_str, " " => "") #Deletes all spaces
x_name,x_index = split(x_str,split_conv[1]) #splits raw variable name+ sets and index
x_name = replace(x_name, split_conv[2] => "")
x_name,s_set = split(x_name,split_conv[3])#splits raw variable name and sets
x_set = split(s_set,split_conv[4])
x_index = replace(x_index, split_conv[2] => "")
x_index = replace(x_index, split_conv[3] => "")
x_index = split(x_index,split_conv[4])
return (x_name,x_set,x_index)
else
println("Var base_name not properly defined. Special Syntax required in form var[s=set]: ")
end
end
The next functions create the columns and the index values plus columns for the primal solution ("Value").
function create_columns(x)
col_ind=[String(var_info(x)[2][col]) for col in 1:size(var_info(x)[2])[1]]
cols = append!(["Value"],col_ind)
return cols
end
function create_index(x)
col_ind=[String(var_info(x)[3][ind]) for ind in 1:size(var_info(x)[3])[1]]
index = append!([string(value(x))],col_ind)
return index
end
function create_sol_matrix(varss,model)
nested_sol_array=[create_index(xx) for xx in all_variables(model) if varss[1]==var_info(xx)[1]]
sol_array=hcat(nested_sol_array...)
return sol_array
end
Finally, the last function creates the Dict which holds all results of the variables in DataFrames in the previously mentioned style:
function create_var_dict(model)
Variable_dict=Dict(vars[1]
=>DataFrame(Dict(vars[2][1][cols]
=>create_sol_matrix(vars,model)[cols,:] for cols in 1:size(vars[2][1])[1]))
for vars in unique([[String(var_info(x)[1]),[create_columns(x)]] for x in all_variables(model)]))
return Variable_dict
end
When those functions are added to your script, you can simply retrieve all the solutions of the variables after the optimization by calling create_var_dict():
var_dict = create_var_dict(model)
Be aware: they are nested functions. When you change the naming convention, you might have to update the other functions as well. If you add more comments you have to avoid using [, ], and ,.
This solution is obviously far from optimal. I believe there could be a more efficient solution falling back to MOI.

How to filter out one method call from many others with `expect().to receive()` in RSpec

I have such a code:
class ClassB
def print_letter(arg)
end
end
class ClassA
def self.my_method
ClassB.print_letter("a")
ClassB.print_letter("b")
end
end
RSpec.describe ClassA do
describe "self.my_method" do
it "prints a" do
allow(ClassB)
expect(ClassB).to receive(:print_letter).once.with("a")
described_class.my_method
end
end
end
And I'm getting a failure:
#<ClassB (class)> received :print_letter with unexpected arguments
expected: ("a")
got: ("b")
Can I do anything with that? Is there any way to force the receive method to analyze all method calls and pick the one where arguments match, not only the last one? BTW, this behavior seems confusing to me.
It's a good practice to give one responsability to one method.
In your case I guess you would like to test that your method return "A" and also "B".
I will recommend you to write a method which will return "A" And another one which return "B".
def print_a
ClassB.print_letter("a")
end
def print_b
ClassB.print_letter("b")
end
def self.my_method
print_a
print_b
end
And then just test your methods separatly, for example:
it " Print a" do
expect(print_a).to eq 'a'
end
In this way you don't need to test your self.my_method, it will Be overkill.

How to filter out "Nothing" values from Elm Array?

I'd like to define the following function:
compactAndConvertToList : Array (Maybe String) -> List String
This function should remove all Nothing appearances in the given array, and convert it to List.
I came up with the solution below, but it feels dirty a bit.
Is there a better way to achieve this?
import Graphics.Element exposing (..)
import Array
model : Array.Array (Maybe String)
model = Array.fromList [ Just "Hello", Just "Stack", Nothing, Just "Overflow" ]
compactAndConvertToList : Array.Array (Maybe String) -> List String
compactAndConvertToList maybeStrings =
maybeStrings
|> Array.filter (\x -> x /= Nothing)
|> Array.map (Maybe.withDefault "")
|> Array.toList
main =
model
|> compactAndConvertToList
|> show
If your final result is a list, you are probably better to just convert your array to a list, and then operate on it.
import Array
compactAndConvertToList : Array.Array (Maybe String) -> List String
compactAndConvertToList = Array.toList >> List.filterMap identity
If you're not comfortable with higher-order functions, you could write it like this:
compactAndConvertToList arr = List.filterMap identity (Array.toList arr)
We take our array, apply toList to it, then apply filterMap with it. Filter map takes a function which produces a maybe, and applies it to every element in the list, discarding the Nothing cases. We just apply the identity functions, which discards the Nothing values that are already there.
In general, Arrays are good for fast random access, but if you're traversing the entire collection a bunch of times, Lists will tend to be faster. But, always start by doing it the clear way, then optimize if you need to.
Maybe this is a better answer as in 2018. Use an elm-community/maybe-extra package.
Example:
Maybe.Extra.values [Just 1, Nothing, Just 3]
Returns:
[1,3]

How to check if a TypedPipe or a ValuePipe are empty in Scalding?

In Scalding, suppose you have a TypedPipe[Long] or ValuePipe[Long]. How would you go about checking whether they are empty in the most elegant/efficient way?
Currently testing the following:
val isTPEmpty: Boolean = typePipe.equals(TypedPipe.empty)
val isVPEmpty: Boolean = valuePipe.equals(EmptyValue)
Or, to make it more generic:
def isTypedPipeEmpty[A](typedPipe: TypedPipe[A]): Boolean = {
val emptyTP: TypedPipe[A] = TypedPipe.empty
typedPipe.equals(emptyTP)
}
UPDATE: this doesn't work (will return false for an empty TypedPipe). Appreciate any inputs.
After speaking to several people on this, there is no straight solution simply because a TypedPipe is distributed, and checking whether it is empty is "expensive", therefore one should avoid this as much as possible.
If you absolutely have no choice, what worked for me was something "ugly" as creating a temporary empty TypedPipe, then calling mapWithValue on my ValuePipe, and if it is empty do X, otherwise do Y. Something like:
TypedPipe.from(List()).mapWithValue(valuePipe) { case (temp, valuePipe) => if (valuePipe.isEmpty) doX else doY }
But again, cumbersome.

Ruby on rails if condition when adding new data to a sql table

I'm pretty new to ruby & ruby on rails and I have a little question :
I want to set a boolean to true if the value of the entry is higher than X and another boolean to true if this value if lower than Y.
I don't really know where to do the code for this or what's the best way to do it.
To be clear, I have a form(made with a scaffold) where I ask a value and depending on this value one of the 2 boolean might be set to true.
Thanks for your help!
you can put this on your controller. I would assume that the form is on controller#create where you would have:
boolean = true if #model.value > x
boolean2 = true if #model.value < y
then save it on db with #model.save
Assuming those booleans are attributes of the same model you have value on, it seems you could do this in before_save callback:
class SomeModel < ActiveRecord::Base
# Your X & Y with some example values
X_VALUE = 5
Y_VALUE = 10
before_save do
self.boolean1 = self.value > X_VALUE
self.boolean2 = self.value < Y_VALUE
true # callback needs to return true, otherwise save will fail
end
end
With above implementation even if you update the value, the proper boolean values will change as well. I hope this helps, as it's made on some assumptions. If not let me know, we'll figure something out.