Related
So i discovered this ruby behaviour, which kept me going crazy for over an hour. When I pass a hash to a function which has a default value for hash AND a keyword argument, it seems like the reference doesn't get passed correctly. As soon as I take away the default value OR the keyword argument, the function behaves as expected. Am I missing some obvious ruby rule here?
def change_hash(h={}, rand: om)
h['hey'] = true
end
k = {}
change_hash(k)
k
#=> {}
It works fine as soon as I take out the default or the keyword arg.
def change_hash(h, rand: om)
h['hey'] = true
end
k = {}
change_hash(k)
k
#=> {'hey' => true}
def change_hash(h={})
h['hey'] = true
end
k = {}
change_hash(k)
k
#=> {'hey' => true}
EDIT
Thanks for your answers. Most of you pointed out that ruby parses the hash as a keyword argument in some cases. However, I am talking about the case when a hash has string keys. When I pass the hash, it seems like the value that gets passed is correct. But modifying the hash inside the function doesn't modify the original hash.
def change_hash(hash={}, another_arg: 300)
puts "another_arg: #{another_arg}"
puts "hash: #{hash}"
hash['hey'] = 3
end
my_hash = {"o" => 3}
change_hash(my_hash)
puts my_hash
Prints out
another_arg: 300
hash: {"o"=>3}
{"o"=>3}
TL;DR ruby allows passing hash as a keyword argument as well as “expanded inplace hash.” Since change_hash(rand: :om) must be routed to keyword argument, so should change_hash({rand: :om}) and, hence, change_hash({}).
Since ruby allows default arguments in any position, the parser takes care of default arguments in the first place. That means, that the default arguments are greedy and the most amount of defaults will take a place.
On the other hand, since ruby lacks pattern-matching feature for function clauses, parsing the given argument to decide whether it should be passed as double-splat or not would lead to huge performance penalties. Since the call with an explicit keyword argument (change_hash(rand: :om)) should definitely pass :om to keyword argument, and we are allowed to pass an explicit hash {rand: :om} as a keyword argument, Ruby has nothing to do but to accept any hash as a keyword argument.
Ruby will split the single hash argument between hash and rand:
k = {"a" => 42, rand: 42}
def change_hash(h={}, rand: :om)
h[:foo] = 42
puts h.inspect
end
change_hash(k);
puts k.inspect
#⇒ {"a"=>42, :foo=>42}
#⇒ {"a"=>42, :rand=>42}
That split feature requires the argument being cloned before passing. That is why the original hash is not being modified.
This is particularly tricky case in Ruby indeed.
In your example you have optional argument which is a hash and you have an optional keyword argument at the same time. In this situation if you pass only one hash, Ruby interprets it as a hash which contains keyword arguments. Here is the code to clarify:
change_hash({rand1: 'om'})
# ArgumentError: unknown keyword: rand1
To work around this you can pass two separate hashes into the method with second one (the one for keyword arguments) being empty:
def change_hash(h={}, rand: 'om')
h['hey'] = true
end
k = {}
change_hash(k, {})
k
#=> {'hey' => true}
From the practical point of view it is better to avoid metdhod signature like that in production code, because it is very easy to make an error while using the method.
I'm learning Clojure and enjoying it but find an inconsistency in Records that puzzles me: why doesn't the default map constructor (map->Whatever) check for data integrity when creating a new Record? For instance:
user=> (defrecord Person [first-name last-name])
#<Class#46ffda99 user.Person>
user=> (map->Person {:first-name "Rich" :last-name "Hickey"})
#user.Person {:first-name "Rich" :last-name "Hickey"}
user=> (map->Person {:first-game "Rich" :last-name "Hickey"})
#user.Person {:first-game "Rich" :first-name nil :last-name "Hickey"}
I believe the Map is not required to define all the fields in the Record definition and it is also allowed to contain extra fields that aren't part of the Record definition. Also I understand that I can define my own constructor which wraps the default constructor and I think a :post condition can then be used to check for correct (and comprehensive) Record creation (have not been successful in getting that to work).
My question is: Is there an idiomatic Clojure way to verify data during Record construction from a Map? And, is there something that I'm missing here about Records?
Thank you.
I think your comprehensiveness requirement is already quite specific, so nothing built-in I know of covers this.
One thing you can do nowadays is use clojure.spec to provide an s/fdef for your constructor function (and then instrument it).
(require '[clojure.spec.alpha :as s]
'[clojure.spec.test.alpha :as stest])
(defrecord Person [first-name last-name])
(s/fdef map->Person
:args (s/cat :map (s/keys :req-un [::first-name ::last-name])))
(stest/instrument `map->Person)
(map->Person {:first-name "Rich", :last-name "Hickey"})
(map->Person {:first-game "Rich", :last-name "Hickey"}) ; now fails
(If specs are defined for ::first-name and ::last-name those will be checked as well.)
Another option is to use Plumatic Schema to create a wrapper "constructor" function specifying the allowed keys. For example:
(def FooBar {(s/required-key :foo) s/Str (s/required-key :bar) s/Keyword})
(s/validate FooBar {:foo "f" :bar :b})
;; {:foo "f" :bar :b}
(s/validate FooBar {:foo :f})
;; RuntimeException: Value does not match schema:
;; {:foo (not (instance? java.lang.String :f)),
;; :bar missing-required-key}
The first line defines a schema that accepts only maps like:
{ :foo "hello" :bar :some-kw }
You wrapper constructor would look something like:
(def NameMap {(s/required-key :first-name) s/Str (s/required-key :last-name) s/Str})
(s/defn safe->person
[name-map :- NameMap]
(map->Person name-map))
or
(s/defn safe->person-2
[name-map]
(assert (= #{:first-name :last-name} (set (keys name-map))))
(map->Person name-map))
Essentially I have a evaluate method that takes something like
['+','2','3']
which would evaluate to 5.
It's currently set up like so,
def evaluate(exp)
f = exp[0]
if f == '+' then
return exp[1].to_i + exp[2].to_i
elsif f == '-' then
return exp[1].to_i - exp[2].to_i
elsif f == '*' then
return exp[1].to_i * exp[2].to_i
elsif f == '/' then
return exp[1].to_i / exp[2].to_i
end
end
This works fine, but there has to be a better way to do this without a giant if. Is there a way for me to convert the symbol and use it? How do typically lisp interpreters handle this?
Ruby's all about dynamic programming. In fact this makes your code ridiculously easy:
def evaluate(exp)
exp[1].to_i.send(exp[0], exp[2].to_i)
end
It'd be even easier if those tokens were converted on the way in:
exp.map! do |token|
case (token)
when /\A\-?\d+\z/
token.to_i
else
token
end
end
Then you get this:
def evaluate(exp)
exp[1].send(exp[0], exp[2])
end
Now this presumes you're only supplying valid operations, that you're not doing anything absurd, but for trivial cases it works quite well.
If you've converted everything and you're looking to make this more extensible:
def evaluate(exp)
op, *args = exp
args.reduce(&op.to_sym)
end
Then you can do this on arbitrary lists:
evaluate([ '+', 2, 1, 3, 4 ])
def evaluate(*args)
operation, *numbers = args
numbers.map!(&:to_i)
numbers.first.public_send(operation, numbers.last)
end
If you expect more, than two numbers to be used:
def evaluate(*args)
operation, *numbers = args
numbers.map!(&:to_i).inject(&operation.to_sym)
end
evaluate('+','2','3', 4, 5, 6)
#=> 20
You can add an initial value if you need to make sure to always return a Numeric from the method (make sure to select any non-zero number, if operation is division or multiplication):
def evaluate(*args)
operation, *numbers = args
initial_value = %w(/ *).include?(operation) ? 1 : 0
numbers.map!(&:to_i).inject(initial_value, &operation.to_sym) #<==== note 0
end
evaluate '+'
#=> 0
from lisp perspective (common lisp to be precise), you can define evaluate as:
(defun evaluate (func &rest args)
(apply func args))
in first line you define evaluate function to take first argument called func, and rest of them (if any) to put in list called args.
then you apply list of arguments stored in args, to function stored in func.
usage examples:
(evaluate #'print "some text, printed by function evaluated by my own code!")
=> "some text, printed by function evaluated by my own code!"
(evaluate #'+ 2 3)
=> 5
this weird looking #' quotes a function name, otherwise "language" would try to evaluate it as it does with all arguments before passing to function.
i also strongly suggest "structure and interpretation of computer programs", at least videos! in the middle there is this great lecture on evaluation, basically heart of (almost) any computer language on 5 whiteboards! (well... green;)
How do typically lisp interpreters handle this?
Lisp uses symbols for global function names. A symbol is a named thing with a few associated informations. One of these can be a function.
CL-USER 42 > (symbol-function '+)
#<Function + 4100044D34>
CL-USER 43 > (funcall (symbol-function '+) 1 2 3 4)
10
So for your simple expression evaluation:
CL-USER 50 > (defun eval-expression (expression)
(destructuring-bind (function &rest arguments)
expression
(apply function arguments)))
EVAL-EXPRESSION
CL-USER 51 > (eval-expression '(+ 1 2 3 4))
10
Symbols itself are organized in packages, which you can imagine as some kind of specialized table. Packages map names to symbols:
CL-USER 52 > (find-symbol "+")
+
:INHERITED
If you would want to evaluate a string, you first have to read the string to convert the string to Lisp data. The reader will look up the symbols, here +, from the current package:
CL-USER 53 > (eval-expression (read-from-string "(+ 1 2 3 4)"))
10
Since you ask how this is typically done.
You create a lookup table from operations to their implementation. And then use the first element to lookup an implementation and pass the remaining elements to that function.
Essentially all you need to implement a lisp is
evaluate
apply
a lookup table
That kernel might even fit into 30–50 lines. I don't think you want us to give you a complete implementation though. That would take all the fun out of writing your own lisp.
I will thus just outline the basic structure…
$table = {
'+' => lambda { |a, b| a + b },
'-' => lambda { |a, b| a - b },
'*' => lambda { |a, b| a * b },
'/' => lambda { |a, b| a.fdiv(b) },
}
def evaluate(args, context = $table)
# A complete implementation of evaluate would make use of apply ...
head, *tail = args
context[head][*tail]
end
def apply
# ...
end
puts evaluate(['/', 4, 3])
# => 1.3333333333333333
It just so happens that your lisp function names and the corresponding Ruby function names are the same. But that will not always be given, using a lookup table is thus a more typical solution. I mapped the lisp / to Ruby's fdiv to demonstrate that.
Fun fact, these first elements are called symbols and actually Ruby uses a very similar mechanism too and hence also has symbols. Internally, Ruby also uses nested lookup tables to organize classes and constants and methods and local variables, et cetera.
A slightly cleaner version of the same:
def evaluate(operation, *numbers)
numbers.map(&:to_i).reduce(operation)
end
evaluate('+', '2', '3') # => 5
evaluate('/', '6', '2', '3') # => 1
There are many different solutions and you can chose one that more pretty for you, there are a few from me:
exp = ['+','2','3']
def evaluate(exp)
exp.map(&:to_i).inject(exp.shift)
end
# > evaluate(exp)
# => 5
def evaluate(operation, *nums)
nums.map(&:to_i).inject(operation)
end
# > evaluate(*exp)
# => 5
This question already has answers here:
What does map(&:name) mean in Ruby?
(17 answers)
Closed 6 years ago.
Why does:
[1,2,3,4,5].map(&:to_s) #=> ["1", "2", "3", "4", "5"]
work but:
[1,2,3,4,5].map(&:*(2))
throws an unexpected syntax error?
& is called the to_proc operator. It calls the to_proc method on the expression that follows it and then passes the resulting Proc to the method as a block.
In the case of &:to_s, :to_s is a Symbol, so it the operator calls Symbol#to_proc. The docs are a little garbled, but suffice it to say that these two expressions are more-or-less equivalent:
my_proc = :to_s.to_proc
my_proc = Proc.new {|obj| obj.to_s }
So the answer to the question "Why doesn't &:*(2) work?" is that the expression that follows the & operator, :*(2), isn't a valid Ruby expression. It makes about as much sense to the Ruby parser as "hello"(2).
There is, by the way, a way to do what you're trying to do:
[1,2,3,4,5].map(&2.method(:*))
# => [2, 4, 6, 8, 10]
In the above code, 2.method(:*) returns a reference to the * method of the object 2 as a Method object. Method objects behave a lot like Proc objects, and they respond to to_proc. However, the above isn't exactly equivalent—it does 2 * n rather than n * 2 (a distinction that doesn't matter if n is also a Numeric)—and it's not any more succinct or readable than {|n| n * 2 }, and so rarely worth the trouble.
Ampersand and object (&:method)
The & operator can also be used to pass an object as a block to a method, as in the following example:
arr = [ 1, 2, 3, 4, 5 ]
arr.map { |n| n.to_s }
arr.map &:to_s
Both the examples above have the same result. In both, the map method takes the arr array and a block, then it runs the block on each element of the array. The code inside the block runs to_s on each element, converting it from integers to strings. Then, the map method returns a new array containing the converted items.
The first example is common and widely used. The second example may look a bit cryptic at first glance. Let's see what's happening:
In Ruby, items prefixed with colon (:) are symbols. If you are not familiar with the Symbol class/data type, I suggest you Google it and read a couple of articles before continuing. All method names in Ruby are internally stored as symbols. By prefixing a method name with a colon, we are not converting the method into a symbol, neither are we calling the method, we are just passing the name of the method around (referencing the method). In the example above, we are passing :to_s, which is a reference to the to_s method, to the ampersand (&) operator, which will create a proc (by calling to_proc under the hood). The proc takes a value as an argument, calls to_s on it and returns the value converted into a string.
Although the :to_s symbol is always the same, when running the map loop, it will refer to the to_s method of the class corresponding to each array item. If we passed an array such as [ 21, 4.453, :foobar, ] to the map method, the to_s method of the Fixnum class would be applied (called) on the first item, the to_s method of the Float class would be applied to the second item and the to_s method of the Symbol class would be applied to the third item. This makes sense because we are not passing the actual to_s method to the ampersand operator, just its name.
Below is an example of creating a proc that takes an argument, calls a method on it and returns the result of the method.
p = :upcase.to_proc
p.call("foo bar")
Output:
=> "FOO BAR"
Let's review what is going on in arr.map &:to_s
At each iteration of map, one item of the array (an integer) is passed to &:to_s
The :to_s symbol (which is a reference to the to_s method) is passed to the & operator, which creates a proc that will take an argument (an array item), call to_s on the argument and return the value converted into string;
The map method returns a new array containing the strings "1", "2", "3", "4" and "5".
This has been asked before, but I can't find an answer that works. I have the following code:
[[13,14,16,11],[22,23]].each do |key,value|
puts key
end
It should in theory print:
0
1
But instead it prints:
13
22
Why does ruby behave this way?
Why does ruby behave this way?
It's because what actually happens internally, when each and other iterators are used with a block instead of a lambda, is actually closer to this:
do |key, value, *rest|
puts key
end
Consider this code to illustrate:
p = proc do |key,value|
puts key
end
l = lambda do |key,value|
puts key
end
Using the above, the following will set (key, value) to (13, 14) and (22, 23) respectively, and the above-mentioned *rest as [16, 11] in the first case (with rest getting discarded):
[[13,14,16,11],[22,23]].each(&p)
In contrast, the following will spit an argument error, because the lambda (which is similar to a block except when it comes to arity considerations) will receive the full array as an argument (without any *rest as above, since the number of arguments is strictly enforced):
[[13,14,16,11],[22,23]].each(&l) # wrong number of arguments (1 for 2)
To get the index in your case, you'll want each_with_index as highlighted in the other answers.
Related discussions:
Proc.arity vs Lambda.arity
Why does Hash#select and Hash#reject pass a key to a unary block?
You can get what you want with Array's each_index' method which returns the index of the element instead of the element itself. See [Ruby'sArray` documentation]1 for more information.
When you do:
[[13,14,16,11],[22,23]].each do |key,value|
before the first iteration is done it makes an assignment:
key, value = [13,14,16,11]
Such an assignment will result with key being 13 and value being 14. Instead you should use each_with_index do |array, index|. This will change the assignment to:
array, index = [[13,14,16,11], 0]
Which will result with array being [13,14,16,11] and index being 0
You have an array of arrays - known as a two-dimensional array.
In your loop, your "value" variable is assigned to the first array, [13,14,16,11]
When you attempt to puts the "value" variable, it only returns the first element, 13.
Try changing puts value to puts value.to_s which will convert the array to a string.
If you want every value, then add another loop block to your code, to loop through each element within the "value" variable.
[[1,2,3],['a','b','c']].each do |key,value|
value.each do |key2,value2|
puts value2
end
end