Named sprintf parameters

I have a question and perhaps a bit of challenge for those with mad
parse skills: Has anyone ever considered named parameters for sprintf/
printf? It would be quite useful (to me at least) to be able to do:

“I am %(name)s.” % { :name => “Tom” ]

Has anyone worked on something like this before? Is there anything
equivalent in the Perl world or other language? I realize we can use
numerals to identify the substitutions, but I feel the labels are much
more readable. Moreover, ultimately it would be interesting to see:

“I am %(name)s.” % binding

Making use of the binding’s local_variables.

The challenge, of course, is to override printf/sprintf to do this
(the binding part gets you extra ruby points :wink: Even more
challenging, augment the C code to handle it and submit it as a patch.

T.

On Feb 28, 2008, at 9:57 AM, Trans wrote:

“I am %(name)s.” % binding

Making use of the binding’s local_variables.

The challenge, of course, is to override printf/sprintf to do this
(the binding part gets you extra ruby points :wink: Even more
challenging, augment the C code to handle it and submit it as a patch.

T.

class String
module M
def % *args
newstuff
super
end
end
include M
end

a @ http://codeforpeople.com/

On Feb 28, 11:57 am, Trans [email protected] wrote:

“I am %(name)s.” % binding

Making use of the binding’s local_variables.

The challenge, of course, is to override printf/sprintf to do this
(the binding part gets you extra ruby points :wink: Even more
challenging, augment the C code to handle it and submit it as a patch.

T.

That exact feature exists in that form in Python today…
http://docs.python.org/lib/typesseq-strings.html

-T3ch Dude

On Feb 28, 11:29 am, ara howard [email protected] wrote:

class String
module M
def % *args
newstuff
super
end
end
include M
end

Can you please explain this? It looks to me like it would allow you to
override a method, but still call the original without using alias.
However, when I try anything like that, it only calls the original
method. The method defined inside of M isn’t called. Is that a 1.9
thing or am I misunderstanding what you meant?

On 2/28/08, yermej [email protected] wrote:

end

However, when I try anything like that, it only calls the original
method. The method defined inside of M isn’t called.

I also couldn’t get Ara’s example to work, so I resorted to
monkeypatching.
(Yes, I know, I am Destroying Ruby…)


p “I am %s.” % “string”
p “I am %s.” % [“array”]
p “I am %s.” % { :name => “hash” }
begin
p “I am %(name)s.” % { :name => “hash” }
rescue ArgumentError => err
p “caught #{err}”
end

puts “\nMonkeyPatching!\n\n”
class String
alias :old_percent :%
def % arg
arr = (arg.kind_of? Array) ? arg : [arg]
target = self.gsub(/%((.+?))/){|m|
raise ArgumentError, “named parameters need hash” unless
arg.kind_of? Hash
arr << arg[$1.to_sym]
“%#{arr.size}$”
}
target.old_percent arr
end
end

p “I am %s.” % “string”
p “I am %s.” % [“array”]
p “I am %s.” % { :name => “hash” }
p “I am %(name)s.” % { :name => “hash” }

p “I am %(name)s %(with)s %(what)s”%{:what => “multiple arguments”,
:with => “including”, :name=> ‘hash’}
p “I am %(name)s %(with)s %1$p”%{:with => “containing”, :name=> ‘hash’}
p “I am %(name)s.” % [:name, “array”]

-Adam

P.S. It actually looks like patching sprintf wouldn’t be too hard, as
long as you disallowed mixing named arguments with any other type.

On 2/28/08, yermej [email protected] wrote:

end

Can you please explain this? It looks to me like it would allow you to
override a method, but still call the original without using alias.
However, when I try anything like that, it only calls the original
method. The method defined inside of M isn’t called. Is that a 1.9
thing or am I misunderstanding what you meant?

No you are right. Including a module in a class effectively inserts
the module above the class in the inheritance chain rather than below,
a class can override methods in a module it includes, not the other
way around. I missed Ara’s point as well.

Along these lines I was scratching my head the other night when I was
reading Russ Olsen’s “Design Patterns in Ruby” and he was showing an
implementation of the Decorator pattern using modules as decorators, I
missed the subtlety that he was using Object#extend to include a
module above the singleton class of a particular object rather than
including it in a ‘real’ class. But I don’t think that technique
applies here.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Feb 28, 2008, at 11:57 AM, Trans wrote:

I have a question and perhaps a bit of challenge for those with mad
parse skills: Has anyone ever considered named parameters for sprintf/
printf? It would be quite useful (to me at least) to be able to do:

“I am %(name)s.” % { :name => “Tom” ]

Has anyone worked on something like this before? Is there anything
equivalent in the Perl world or other language?

I released a gem last year (http://jig.rubyforge.com) that defines
a data structure I nicknamed a Jig that enables this sort of thing
either by parsing a string to get the format or by explicit
construction:

j = Jig.new('I am ', :name)
j2 = Jig.parse(‘I am %{:name:}’)

puts j % { :name => ‘Tom’} # I am Tom
puts j2 % { :name => ‘Alice’} # I am Alice

A Jig is a ordered sequence of objects and ‘gaps’. Simple gaps are
identified as symbols during Jig construction but you can also define
gaps that process anything that is used to fill them:

capitalize = Jig::Gap.new(:name) { |x| x.capitalize }
j2 = Jig.new('before: ', :name, ’ after: ', capitalize)
puts j2 % {:name => ‘bob’ } # ‘before: bob after: Bob’

In that example you can see that all gaps with the same name
are plugged simultaneously. Unplugged gaps are rendered as
the empty string when Jig#to_s is called.

You can also construct a Jig or fill a gap with a proc/lambda/method
which will be evaluated at the time the Jig is rendered to a string:

time = Jig.new("The time is ") {Time.now}
puts time    # The time is Thu Feb 28 18:16:07 -0500 2008
sleep 5
puts time            # The time is Thu Feb 28 18:16:12 -0500 2008

The gem has classes for handling XML, XHTML, and CSS constructs.

I’ve made a few changes since the initialize release but haven’t
gotten around to pushing it a new release to Rubyforge yet…

Gary W.

http://jig.rubyforge.com

Hi,

At Fri, 29 Feb 2008 01:57:23 +0900,
Trans wrote in [ruby-talk:292860]:

I have a question and perhaps a bit of challenge for those with mad
parse skills: Has anyone ever considered named parameters for sprintf/
printf? It would be quite useful (to me at least) to be able to do:

Yes, once I had posted the patch for it, and was rejected.

“I am %(name)s.” % { :name => “Tom” ]

Has anyone worked on something like this before? Is there anything
equivalent in the Perl world or other language? I realize we can use
numerals to identify the substitutions, but I feel the labels are much
more readable. Moreover, ultimately it would be interesting to see:

“I am %(name)s.” % binding

Making use of the binding’s local_variables.

Though I don’t feel it attractive, I believe named parameter is
an important feature for I18N, and IIRC, ruby-gettext has it.

On Fri, Feb 29, 2008 at 01:51:02PM +0900, Nobuyoshi N. wrote:

“I am %(name)s.” % { :name => “Tom” ]
Though I don’t feel it attractive, I believe named parameter is
an important feature for I18N, and IIRC, ruby-gettext has it.

You may find the recently announced Jig library of interest. I was
sitting
next to Gary during something at RubyConf, maybe RejectConf, and he gave
me
a demo. Pretty cool. See http://www.ruby-forum.com/topic/144356

Nobu Nakada
–Greg

On Feb 28, 11:51 pm, Nobuyoshi N. [email protected] wrote:

Hi,

At Fri, 29 Feb 2008 01:57:23 +0900,
Trans wrote in [ruby-talk:292860]:

I have a question and perhaps a bit of challenge for those with mad
parse skills: Has anyone ever considered named parameters for sprintf/
printf? It would be quite useful (to me at least) to be able to do:

Yes, once I had posted the patch for it, and was rejected.

:frowning:

Though I don’t feel it attractive, I believe named parameter is
an important feature for I18N, and IIRC, ruby-gettext has it.

Really? It seems like a nice way to apply parameter to templates to
me. Rather then, say,

xml = %{

#{params[:name]}

}

One could do:

xml = %{

%(name)s

} % params

A little cleaner --and provides a nice means of reusable
interpolation.

I’ll have to look at ruby-gettext.

Thanks,
T.

On Feb 28, 2008, at 1:09 PM, yermej wrote:

end

Can you please explain this? It looks to me like it would allow you to
override a method, but still call the original without using alias.
However, when I try anything like that, it only calls the original
method. The method defined inside of M isn’t called. Is that a 1.9
thing or am I misunderstanding what you meant?

i was just being stupid. you’d need something like this

cfp2:~ > cat a.rb
class String

Percent = instance_method ‘%’ unless defined? Percent

def % *a, &b
a.flatten!
string =
case a.last
when Hash
expand a.pop
else
self
end
if a.empty?
string
else
Percent.bind(string).call(*a, &b)
end
end

def expand! vars = {}
loop do
changed = false
vars.each do |var, value|
var = var.to_s
var.gsub! %r/[^a-zA-Z0-9_]/, ‘’
[
%r/$#{ var }\b/,
%r/@#{ var }\b/,
%r/${\s*#{ var }\s*}/,
%r/@{\s*#{ var }\s*}/,
].each do |pat|
changed = gsub! pat, “#{ value }”
end
end
break unless changed
end
self
end

def expand opts = {}
dup.expand! opts
end

end

puts( ‘I am @name’.expand(:name => ‘Ara’) )
puts( ‘I am @name’ % {:name => ‘Ara’} )

cfp2:~ > ruby a.rb
I am Ara
I am Ara

a @ http://codeforpeople.com/

On Feb 28, 7:13 pm, ara howard [email protected] wrote:

  super
   case a.last

end
%r/${\s*#{ var }\s*}/,
def expand opts = {}
I am Ara
Hmmm… this looks more like an answer to the interpolation question
asked in a earlier thread. Is this a full-fledge implementation? Would
it be a better solution then:

def String.interpolate(&str)
eval “%{#{str.call}}”, str.binding
end

T.

On Feb 28, 3:42 pm, “Adam S.” [email protected] wrote:

   super

(Yes, I know, I am Destroying Ruby…)
end
}
:with => “including”, :name=> ‘hash’}
p “I am %(name)s %(with)s %1$p”%{:with => “containing”, :name=> ‘hash’}
p “I am %(name)s.” % [:name, “array”]

-Adam

Looks like you get the prize :wink:

I would like to add this to Facets, though under a slightly different
naming of course. Not sure what though #printfv #sprintfv ?

Thanks!
T.

I forgot one important point… Don’t make me use a hash if I don’t want
to! (hence mapping the hash to array or just using an array if no hash)

Adam S. wrote:

puts “\nMonkeyPatching!\n\n”
class String
alias :old_percent :%
def % arg
arr = (arg.kind_of? Array) ? arg : [arg]
target = self.gsub(/%((.+?))/){|m|
raise ArgumentError, “named parameters need hash” unless
arg.kind_of? Hash
arr << arg[$1.to_sym]
“%#{arr.size}$”
}
target.old_percent arr
end
end

Don’t forget the escaped %, and also, I propose the format %foo:s
instead of %(foo)s. And heres some more code to chew on, based off of
Adam’s:

class String
alias old_percent %
def % arg
names = []
target = gsub /(^|[^%])%(\w+):confused: do
name, space = $2, $1 # don’t forget to replace whatever wasn’t %
names << name unless names.include? name
“#{space}%#{names.rindex(name) + 1}$”
end
args = case arg
when Hash: names.map {|n| arg[n] or arg[n.to_sym]} #keys are often
symbols
when Array: arg
else [arg]
end
old_percent args
end
end

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs