Lua 5.1.4
For example:
bar = {}
bar.name = 'test'
bar['123.com'] = function(self) print(self.name) end
I can't call the method like below:
bar:['123.com']()
stdin:1: '<name>' expected near '['
Althought below works:
bar['123.com'](bar)
But I this is somehow ugly. Is there a syntax sugar for this situation?
Or if it really cannot do it, will Lua team add this syntax in future?
Or they did this intentionally?
No we can not call the method like you want. Your suppose to call the method as following syntax only.
bar['123.com'](bar)
Just make an alias that doesn't begin with numbers and use that.
bar.name123 = bar['123name']
bar:name123()
Nobody knows what the Lua maintainers will add in future versions (they're pretty close-mouthed), but my guess would be that it's unlikely they'll add it without at least being asked—and I've never seen a request for such a thing on the Lua mailing list...
My intuition, though, is that this functionality seems obscure: how often do people really want to call methods with "weird" names like that? Given that, and that the "workaround" really isn't bad at all, it's unclear whether it's worth adding complexity to the implementation to support such a feature (unless it's completely trivial).
If you want to try asking, you should post about it to the Lua mailing list, which is where most such discussion takes place: lua-l#lists.lua.org
You might want to consider something like below (you can test online at http://www.lua.org/cgi-bin/demo):
bar = {}
bar.name = 'test'
bar['123.com'] = function(self) print(self.name) end
bar2 = setmetatable({}, {
__index = function(t, key)
return function(...)
return bar[key](bar, ...)
end
end
})
bar2['123.com']()
-- output: test
You can also change bar itself to behave in a similar way, only you must do it before you assign any values - e.g.:
bar = {}
local privatekey = {}
setmetatable(bar, {
__index = function(t, key)
local value = rawget(t, privatekey)[key]
if type(value) ~= 'function' then
return value
end
return function(...)
return value(t, ...)
end
end,
__newindex = function(t, key, value)
rawset(t, privatekey, rawget(t, privatekey) or {})
rawget(t, privatekey)[key] = value
end
})
bar.name = 'test'
bar['123.com'] = function(self) print(self.name) end
bar['123.com']()
-- output: test
You can use a closure:
bar = {}
bar.name = 'test'
bar['123.com'] = function() print(bar.name) end
bar['123.com']()
function bar:addBar(name)
bar[name] = function() print(self.name) end
end
bar:addBar('456.com')
bar['456.com']()
Oh god...
bar = {
['123.com'] = function(self)
print("YAY!", self)
end
}
function f()
print(bar)
bar:WHATTHE()
end
function crazy(f, patt, repl)
local f_str = string.dump(f)
local newf_str = string.gsub(f_str, "WHATTHE", "123.com")
assert(#newf_str == #f_str, "replacement must be of same length")
local newf = loadstring(newf_str)
setfenv(newf, getfenv(f))
return newf
end
f = crazy(f, "WHATTHE", "123.com")
f()
--[[ Output:
table: 005EB688
YAY! table: 005EB688
]]
Don't do this - ever.
Related
Is there another way to write 'a'.next.next? I've looked all over and can't seem to find it.
I've tried multiplying the .next but I keep getting errors.
Well, this might not be a good idea in the case here, but if you're looking to chain a method n times in general, you can do something like this:
2.times.inject('a') { |s| s.next }
# => 'c'
20.times.inject('a') { |s| s.next }
# => 'u'
This starts with the value 'a', runs a block that calls next, then each successive result is fed back into the block.
For what it's worth, monkey-patching String can be fine for trivial scripts, but personally I'd try to look for other solutions first, like just adding a utility function to your class/module:
def repeat_next(str, n = 1)
n.times.inject(str) { |s| s.next }
end
A shortcut for your specific problem, (a.ord + 2).chr, potentially exists, although it's not the same thing.
You can just redefine String.next like this:
class String
alias_method :next1, :next
def next(n = 1)
str = self
for i in 1..n
str = str.next1
end
str
end
end
puts 'a'.next
puts 'a'.next(2)
puts 'a'.next(20)
If you're looking for a more succinct way of doing this, you could use: ('a'.ord + 2).chr. This will convert 'a' to a numerical representation (with the "ord" method), increment it by two, then converts it back to the character representation (with "chr").
You can monkey-patch the String class in ruby to add a method to do this for you:
class String
def get_nth_char(n)
current = self
while n > 0 do
current = current.next
n = n - 1
end
current
end
end
So you can do 'a'.get_nth_char(2) # => 'c'
I'm fairly sure that in Lua, you can use a given metatable's __index, __newindex, and __call to roughly replicate Ruby's method_missing. And I somewhat have:
function method_missing(selfs, func)
local meta = getmetatable(selfs)
local f
if meta then
f = meta.__index
else
meta = {}
f = rawget
end
meta.__index = function(self, name)
local v = f(self, name)
if v then
return v
end
local metahack = {
__call = function(self, ...)
return func(selfs, name, ...)
end
}
return setmetatable({}, metahack)
end
setmetatable(selfs, meta)
end
_G:method_missing(function(self, name, ...)
if name=="test_print" then
print("Oh my lord, it's method missing!", ...)
end
end)
test_print("I like me some method_missing abuse!")
print(this_should_be_nil)
My problem is this: While the syntax is similar, and I can certainly use it to replicate the functionality, it introduces a breaking error. Every single variable that you use in the context of the table you apply a method_missing to is never nil, since I have to return an object that can be called in order to pass the buck of the potential call from the index function to an actual call.
i.e. After defining a global method_missing as above, attempting to call undefined method 'test_print' runs as expected, but the value of test_print when indexed is non-nil, and other methods/variables that aren't responded to, like this_should_be_nil are non-nil.
So is it possible to avoid this pitfall? Or can the syntax not be bent to support this modification without modifying the language source itself? I imagine the difficulty arises in how in Ruby, indexing and calling are analogous, whereas in Lua they are distinct.
You can avoid this problem by making nil value callable.
Unfortunatelly, this can be done only from host code (i.e., C program), not from Lua script.
Pascal code:
function set_metatable_for_any_value_function(L: Plua_State): Integer; cdecl;
begin // set_metatable_for_any_value(any_value, mt)
lua_setmetatable(L, -2);
Result := 0;
end;
procedure Test_Proc;
var
L: Plua_State;
const
Script =
'set_metatable_for_any_value(nil, ' +
' { ' +
' __call = function() ' +
' print "This method is under construction" ' +
' end ' +
' } ' +
') ' +
'print(nonexisting_method == nil) ' +
'nonexisting_method() ';
begin
L := luaL_newstate;
luaL_openlibs(L);
lua_pushcfunction(L, lua_CFunction(#set_metatable_for_any_value_function));
lua_setglobal(L, 'set_metatable_for_any_value');
luaL_dostring(L, Script);
lua_close(L);
end;
Output:
true
This method is under construction
You have identified the problem well: it is not, as far as I know, possible to solve that issue in pure Lua.
EDIT: I was wrong, you can by making nil callable. See other answers. It is still a bad idea IMO. The main use case for method_missing is proxy objects and you can solve that in another way. method_missing on Kernel (Ruby) / _G (Lua) is terrible :)
What you could do is only handle some methods, for instance if you know you expect methods that start by test_:
local function is_handled(method_name)
return method_name:sub(1,5) == "test_"
end
function method_missing(selfs, func)
local meta = getmetatable(selfs)
local f
if meta then
f = meta.__index
else
meta = {}
f = rawget
end
meta.__index = function(self, name)
local v = f(self, name)
if v then
return v
end
if is_handled(name) then
local metahack = {
__call = function(self, ...)
return func(selfs, name, ...)
end
}
return setmetatable({}, metahack)
end
end
setmetatable(selfs, meta)
end
_G:method_missing(function(self, name, ...)
if name=="test_print" then
print("Oh my lord, it's method missing!", ...)
end
end)
test_print("I like me some method_missing abuse!")
print(this_should_be_nil)
Now maybe the question should be: why do you want to replicate method_missing, and can you avoid it? Even in Ruby it is advised to avoid the use of method_missing and prefer dynamic method generation when possible.
So with the tip from #lhf, I've managed a passable double (from what I can tell) of method_missing. In the end, I developed the following:
local field = '__method__missing'
function method_missing(selfs, func)
local meta = getmetatable(selfs)
local f
if meta then
f = meta.__index
else
meta = {}
f = rawget
end
meta.__index = function(self, name)
local v = f(self, name)
if v then
return v
end
rawget(self, name)[field] = function(...)
return func(self, name, ...)
end
end
setmetatable(selfs, meta)
end
debug.setmetatable(nil, { __call = function(self, ...)
if self[field] then
return self[field](...)
end
return nil
end, __index = function(self, name)
if name~=field then error("attempt to index a nil value") end
return getmetatable(self)[field]
end, __newindex = function(self, name, value)
if name~=field then error("attempt to index a nil value") end
getmetatable(self)[field] = value
end} )
_G:method_missing(function(self, name, ...)
local args = {...}
if name=="test_print" then
print("Oh my lord, it's method missing!", ...)
return
elseif args[1] and string.find(name, args[1]) then --If the first argument is in the name called...
table.remove(args, 1)
return unpack(args)
end
end)
test_print("I like me some method_missing abuse!")
test_print("Do it again!")
print(test_print, "method_missing magic!")
print(this_should_be_nil == nil, this_should_be_nil() == nil)
print(conditional_runs("runs", "conditionally", "due to args"))
print(conditional_runs("While this does nothing!")) --Apparently this doesn't print 'nil'... why?
Output:
Oh my lord, it's method missing! I like me some method_missing abuse!
Oh my lord, it's method missing! Do it again!
nil method_missing magic!
true true
conditionally due to args
This snippet lets you use method_missing pretty similarly to how you can in Ruby (with no response checking whatsoever, though). It's similar to my initial response, except it 'passes the buck' through nil's metatable, something I thought I couldn't do. (thanks for the tip!) But as #greatwolf says, there is probably no reason to ever use a construct like this in Lua; the same dynamism can probably be achieved through more clear metamethod manipulations.
I am wandering if it is OK to use ruby's implicit return value when using []= method
[]= uses rb_hash_aset and it is returning val - http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-5B-5D-3D
here is a little code to demonstrate what I mean:
require 'benchmark'
CACHE = {}
def uncached_method(key)
warn "uncached"
rand(100)
end
def cached(key)
CACHE[key] || (CACHE[key] = uncached_method(key))
end
def longer_cached(key)
return CACHE[key] if CACHE[key]
CACHE[key] = uncached_method(key)
CACHE[key]
end
Benchmark.bm(7) do |x|
y = rand(10000)
cached(y)
x.report("shorter:") { 10000000.times do cached(y) end }
x.report("longer:") { 10000000.times do longer_cached(y) end }
end
of course longer_cached is slower because it does two hash lookups to return cached value, but when you read it line by line it makes more sense then the cached method.
I think using implicit returns is one of the things that makes ruby awesome, but I have always questioned their use when setting values.
So my question is: would you use implicit return from (hash[key] = val)?
You can also use the ||= operator in this case.
CACHE[key] ||= uncached_method(key)
This is a very common idiom.
Just because nobody mentioned it so far: you are not relying on the return value of Hash#[]=. That return value gets ignored anyway:
class ReturnFortyTwo
def []=(*)
return 42
end
end
r = ReturnFortyTwo.new
r[23] = 'This is the value that is going to be returned, not 42'
# => 'This is the value that is going to be returned, not 42'
In Ruby, assignment expressions always evaluate to the value that is being assigned. No exception. That is guaranteed by the Language Specification. So, I don't see anything wrong with relying on that.
In this case, shorter one is preferrable. Ussually = used in a subexpression is frowned upon, but here it's OK.
I'd keep it as simple and clean as possible (it's faster, too):
def cached(key)
value = CACHE[key]
unless value
value = uncached_method(key)
CACHE[key] = value
end
value
end
I want to write code for Ruby in a more Ruby-like style and ran into a problem when working with argument passing.
I have to see if ABC is nil or not. If ABC is nil, I would pass another symbol into dosomething, if not I would pass another type of hash value to compute.
Since Ruby is not like Java, it can pass a different type of argument (different keys).
How can I make the following code more beautiful?
Merging do_manything, do_otherthings, do_manythings_again into a single function is not the answer I because I would call dosomething in many places in my code:
if ABC.nil?
Apple.dosomething (:foo => DEF) { |a|
a.do_manything
a.do_otherthings
a.do_manythings_again
}
else
Apple.dosomething (:bar => ABC) { |a|
a.do_manything
a.do_otherthings
a.do_manythings_again
}
end
Using the ternary operator.
Apple.dosomething (ABC.nil? ? {foo:DEF} : {bar:ABC}) do |a|
a.do_manything
a.do_otherthings
a.do_manythings_again
end
Here is the format
condition ? return_if_true : return_if_false
You can either switch the hash you send:
opts = ABC.nil? ? {foo:DEF} : {bar:ABC}
Apple.dosomething(opts) do |a|
do_many_things
do_other_things
do_many_things_again
end
...or you can pass a lambda as the block:
stuff_to_do = ->(a) do
do_many_things
do_other_things
do_many_things_again
end
if ABC.nil?
Apple.dosomething(foo:DEF,&stuff_to_do)
else
Apple.dosomething(bar:ABC,&stuff_to_do)
end
You could do this:
options = if ABC.nil? then { foo: DEF } else { bar: ABC } end
Apple.do_something options do |apple|
apple.instance_eval do
do_many_things
do_other_things
do_many_things_again
end
end
By convention, words in names and identifiers are separated by underscores (_) and do/end is used for multiple-line blocks.
Also, I believe this question belongs on Code Review.
I am trying to mess around a little bit with Ruby. Therefor I try to implement the algorithms (given in Python) from the book "Programming Collective Intelligence" Ruby.
In chapter 8 the author passes a method a as parameter. This seems to work in Python but not in Ruby.
I have here the method
def gaussian(dist, sigma=10.0)
foo
end
and want to call this with another method
def weightedknn(data, vec1, k = 5, weightf = gaussian)
foo
weight = weightf(dist)
foo
end
All I got is an error
ArgumentError: wrong number of arguments (0 for 1)
The comments referring to blocks and Procs are correct in that they are more usual in Ruby. But you can pass a method if you want. You call method to get the method and .call to call it:
def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) )
...
weight = weightf.call( dist )
...
end
You want a proc object:
gaussian = Proc.new do |dist, *args|
sigma = args.first || 10.0
...
end
def weightedknn(data, vec1, k = 5, weightf = gaussian)
...
weight = weightf.call(dist)
...
end
Just note that you can't set a default argument in a block declaration like that. So you need to use a splat and setup the default in the proc code itself.
Or, depending on your scope of all this, it may be easier to pass in a method name instead.
def weightedknn(data, vec1, k = 5, weightf = :gaussian)
...
weight = self.send(weightf)
...
end
In this case you are just calling a method that is defined on an object rather than passing in a complete chunk of code. Depending on how you structure this you may need replace self.send with object_that_has_the_these_math_methods.send
Last but not least, you can hang a block off the method.
def weightedknn(data, vec1, k = 5)
...
weight =
if block_given?
yield(dist)
else
gaussian.call(dist)
end
end
...
end
weightedknn(foo, bar) do |dist|
# square the dist
dist * dist
end
But it sounds like you would like more reusable chunks of code here.
You can pass a method as parameter with method(:function) way. Below is a very simple example:
def double(a)
return a * 2
end
=> nil
def method_with_function_as_param( callback, number)
callback.call(number)
end
=> nil
method_with_function_as_param( method(:double) , 10 )
=> 20
The normal Ruby way to do this is to use a block.
So it would be something like:
def weightedknn(data, vec1, k = 5)
foo
weight = yield(dist)
foo
end
And used like:
weightedknn(data, vec1) { |dist| gaussian( dist ) }
This pattern is used extensively in Ruby.
You can use the & operator on the Method instance of your method to convert the method to a block.
Example:
def foo(arg)
p arg
end
def bar(&block)
p 'bar'
block.call('foo')
end
bar(&method(:foo))
More details at http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html
You have to call the method "call" of the function object:
weight = weightf.call( dist )
EDIT: as explained in the comments, this approach is wrong. It would work if you're using Procs instead of normal functions.
I would recommend to use ampersand to have an access to named blocks within a function. Following the recommendations given in this article you can write something like this (this is a real scrap from my working program):
# Returns a valid hash for html form select element, combined of all entities
# for the given +model+, where only id and name attributes are taken as
# values and keys correspondingly. Provide block returning boolean if you
# need to select only specific entities.
#
# * *Args* :
# - +model+ -> ORM interface for specific entities'
# - +&cond+ -> block {|x| boolean}, filtering entities upon iterations
# * *Returns* :
# - hash of {entity.id => entity.name}
#
def make_select_list( model, &cond )
cond ||= proc { true } # cond defaults to proc { true }
# Entities filtered by cond, followed by filtration by (id, name)
model.all.map do |x|
cond.( x ) ? { x.id => x.name } : {}
end.reduce Hash.new do |memo, e| memo.merge( e ) end
end
Afterwerds, you can call this function like this:
#contests = make_select_list Contest do |contest|
logged_admin? or contest.organizer == #current_user
end
If you don't need to filter your selection, you simply omit the block:
#categories = make_select_list( Category ) # selects all categories
So much for the power of Ruby blocks.
Similarly to a Proc or a method call, you can also pass a lambda as weightf parameter :
def main
gaussian = -> (params) {
...
}
weightedknn(data, vec1, k = 5, gaussian, params)
# Use symbol :gaussian if method exists instead
end
def weightedknn(data, vec1, k = 5, weightf, params)
...
weight = weightf.call(params)
...
end
you also can use "eval", and pass the method as a string argument, and then simply eval it in the other method.