Handling of arbitrary length arrays in arguments to printf builtin

Hi Folks,

I'm porting an error reporting system from Perl to Ruby as a way of getting up the Ruby learning curve. Apologies in advance if my question has already been answered - I did a cursory search and got no hits.

I'm running up against my lack of detailed Ruby knowledge, and I was wondering if someone could provide some advice - I'm sure that Ruby supports what I want to do.

In the Perl world, I had a system where I could specify an error message and a set of parameters to that message, as follows:

  do_error(message, [parameters]);

for example:

  do_error("param 1: '%s', param 2 : '%s', param 3 : '%s'",
       "detail1", "detail2", "detail3");

would give the result:

  param 1: 'detail1', param 2 : 'detail2', param 3 : 'detail3'

The perl implementation looks like this:

sub do_error {
   my ($message, @params) = @_;

   printf($message, @params);
}

As you can see, the idea is that the user is able to specify both their error message and where in their message string various parameters appear.

In this case, Perl was very obliging because parameter lists are simply arrays. Passing an array into printf became equivalent to passing the appropriate number of parameters.

So, in Ruby, I have the following code:

printf "#{@message}\n", @params

Where class instance variable @params is an array. I have verified that it really is an array and not getting mangled somewhere in my class by using '@params.inspect'.

Unfortunately, it seems that Ruby is treating the array in what Perl folks would call a "scalar context". The printf is getting a mangled version of the array contents and this is triggering a runtime error from Ruby.

For example:

  @params = ["foo", "bar"]
  print @params.inspect
  >>> ["foo", "bar"]

  printf("%s %s", @params)
  >>> ErrorClasses.rb:32:in `printf': too few arguments (ArgumentError)

I did some experiments, and it appears that printf is actually seeing:
  "foobar"
instead of the array of 2 elements. If I have the right number of '%s' conversion codes in the format string, I get this message because Ruby thinks I've only passed on parameter, not many.

For the record, here's the Ruby version I'm using

  BigWhite:nick> ruby -v
  ruby 1.8.4 (2005-12-24) [i686-darwin8.8.1]

What would Ruby's code look like to achieve what I want?

Thanks in advance,

Nick

Nick Pavey wrote:

Hi Folks,

I'm porting an error reporting system from Perl to Ruby as a way of getting up the Ruby learning curve. Apologies in advance if my question has already been answered - I did a cursory search and got no hits.

I'm running up against my lack of detailed Ruby knowledge, and I was wondering if someone could provide some advice - I'm sure that Ruby supports what I want to do.

In the Perl world, I had a system where I could specify an error message and a set of parameters to that message, as follows:

    do_error(message, [parameters]);

for example:

    do_error("param 1: '%s', param 2 : '%s', param 3 : '%s'",
             "detail1", "detail2", "detail3");

would give the result:

    param 1: 'detail1', param 2 : 'detail2', param 3 : 'detail3'

The perl implementation looks like this:

sub do_error {
  my ($message, @params) = @_;

  printf($message, @params);
}

As you can see, the idea is that the user is able to specify both their error message and where in their message string various parameters appear.

In this case, Perl was very obliging because parameter lists are simply arrays. Passing an array into printf became equivalent to passing the appropriate number of parameters.

So, in Ruby, I have the following code:

printf "#{@message}\n", @params

Where class instance variable @params is an array. I have verified that it really is an array and not getting mangled somewhere in my class by using '@params.inspect'.

Unfortunately, it seems that Ruby is treating the array in what Perl folks would call a "scalar context". The printf is getting a mangled version of the array contents and this is triggering a runtime error from Ruby.

For example:

    @params = ["foo", "bar"]
    print @params.inspect
    >>> ["foo", "bar"]

    printf("%s %s", @params)
    >>> ErrorClasses.rb:32:in `printf': too few arguments (ArgumentError)

I did some experiments, and it appears that printf is actually seeing:
    "foobar"
instead of the array of 2 elements. If I have the right number of '%s' conversion codes in the format string, I get this message because Ruby thinks I've only passed on parameter, not many.

For the record, here's the Ruby version I'm using

    BigWhite:nick> ruby -v
    ruby 1.8.4 (2005-12-24) [i686-darwin8.8.1]

What would Ruby's code look like to achieve what I want?

Thanks in advance,

Nick

Welcome to Ruby!

Use the "*" (splat) operator to turn the array into a list of arguments.

printf "%s %s", *@params

The unary asterisk is often called the "unary unarray" operator because it has this effect on an array.

Blind leading the blind here, (I'm a newbie), but I think I understand what
you're trying. Did I completely misunderstand or does this help?

@params = ["foo", "bar"]
print @params.inspect
@params.each { |x| printf x }
output:
["foo", "bar"]foobar

@params = ["foo", "bar"]
print @params.inspect
printf("%s %s", @params, @params)
output:
["foo", "bar"]foobar foobar

···

On 12/27/06, Nick Pavey <nick@skatingloons.com> wrote:

Hi Folks,

I'm porting an error reporting system from Perl to Ruby as a way of
getting up the Ruby learning curve. Apologies in advance if my
question has already been answered - I did a cursory search and got
no hits.

I'm running up against my lack of detailed Ruby knowledge, and I was
wondering if someone could provide some advice - I'm sure that Ruby
supports what I want to do.

In the Perl world, I had a system where I could specify an error
message and a set of parameters to that message, as follows:

       do_error(message, [parameters]);

for example:

       do_error("param 1: '%s', param 2 : '%s', param 3 : '%s'",
                        "detail1", "detail2", "detail3");

would give the result:

       param 1: 'detail1', param 2 : 'detail2', param 3 : 'detail3'

The perl implementation looks like this:

sub do_error {
  my ($message, @params) = @_;

  printf($message, @params);
}

As you can see, the idea is that the user is able to specify both
their error message and where in their message string various
parameters appear.

In this case, Perl was very obliging because parameter lists are
simply arrays. Passing an array into printf became equivalent to
passing the appropriate number of parameters.

So, in Ruby, I have the following code:

printf "#{@message}\n", @params

Where class instance variable @params is an array. I have verified
that it really is an array and not getting mangled somewhere in my
class by using '@params.inspect'.

Unfortunately, it seems that Ruby is treating the array in what Perl
folks would call a "scalar context". The printf is getting a mangled
version of the array contents and this is triggering a runtime error
from Ruby.

For example:

       @params = ["foo", "bar"]
       print @params.inspect
       >>> ["foo", "bar"]

       printf("%s %s", @params)
       >>> ErrorClasses.rb:32:in `printf': too few arguments
(ArgumentError)

I did some experiments, and it appears that printf is actually seeing:
       "foobar"
instead of the array of 2 elements. If I have the right number of '%
s' conversion codes in the format string, I get this message because
Ruby thinks I've only passed on parameter, not many.

For the record, here's the Ruby version I'm using

       BigWhite:nick> ruby -v
       ruby 1.8.4 (2005-12-24) [i686-darwin8.8.1]

What would Ruby's code look like to achieve what I want?

Thanks in advance,

Nick

[snip perfect example of how to ask a question]

Since we have been discussing it recently, everyone should read Nick's entire post and hold it as the perfect example of how to ask questions correctly.

Welcome to Ruby Nick!

James Edward Gray II

···

On Dec 27, 2006, at 1:54 PM, Nick Pavey wrote:

Hi Folks,

I'm porting an error reporting system from Perl to Ruby as a way of getting up the Ruby learning curve. Apologies in advance if my question has already been answered - I did a cursory search and got no hits.

Neat! How would you use that to return "bar stuff" with the following?

@params = ["foo", "bar", "stuff"]
print @params.inspect
printf "%s %s", *@params

I'm not sure that Ri Array#* is quite what I meant to look for.

···

On 12/27/06, Timothy Hunter <TimHunter@nc.rr.com> wrote:

Welcome to Ruby!

Use the "*" (splat) operator to turn the array into a list of arguments.

printf "%s %s", *@params

The unary asterisk is often called the "unary unarray" operator because
it has this effect on an array.

...

I seem to be missing Nick's posting on the news side. I can see it on the mailing list. Is this the gateway again or my news provider?

Regards

  robert

···

On 27.12.2006 21:03, Timothy Hunter wrote:

Nick Pavey wrote:

Hi Folks,

Hi Everyone,

Thanks very much for your fast responses - the "unary unarray" operator works great!

Thanks also for the kind welcome you've given me - it makes a big difference.

Best wishes,

Nick

···

On Dec 27, 2006, at 3:03 PM, Timothy Hunter wrote:

Nick Pavey wrote:

Hi Folks,

I'm porting an error reporting system from Perl to Ruby as a way of getting up the Ruby learning curve. Apologies in advance if my question has already been answered - I did a cursory search and got no hits.

I'm running up against my lack of detailed Ruby knowledge, and I was wondering if someone could provide some advice - I'm sure that Ruby supports what I want to do.

In the Perl world, I had a system where I could specify an error message and a set of parameters to that message, as follows:

    do_error(message, [parameters]);

for example:

    do_error("param 1: '%s', param 2 : '%s', param 3 : '%s'",
             "detail1", "detail2", "detail3");

would give the result:

    param 1: 'detail1', param 2 : 'detail2', param 3 : 'detail3'

The perl implementation looks like this:

sub do_error {
  my ($message, @params) = @_;

  printf($message, @params);
}

As you can see, the idea is that the user is able to specify both their error message and where in their message string various parameters appear.

In this case, Perl was very obliging because parameter lists are simply arrays. Passing an array into printf became equivalent to passing the appropriate number of parameters.

So, in Ruby, I have the following code:

printf "#{@message}\n", @params

Where class instance variable @params is an array. I have verified that it really is an array and not getting mangled somewhere in my class by using '@params.inspect'.

Unfortunately, it seems that Ruby is treating the array in what Perl folks would call a "scalar context". The printf is getting a mangled version of the array contents and this is triggering a runtime error from Ruby.

For example:

    @params = ["foo", "bar"]
    print @params.inspect
    >>> ["foo", "bar"]

    printf("%s %s", @params)
    >>> ErrorClasses.rb:32:in `printf': too few arguments (ArgumentError)

I did some experiments, and it appears that printf is actually seeing:
    "foobar"
instead of the array of 2 elements. If I have the right number of '%s' conversion codes in the format string, I get this message because Ruby thinks I've only passed on parameter, not many.

For the record, here's the Ruby version I'm using

    BigWhite:nick> ruby -v
    ruby 1.8.4 (2005-12-24) [i686-darwin8.8.1]

What would Ruby's code look like to achieve what I want?

Thanks in advance,

Nick

Welcome to Ruby!

Use the "*" (splat) operator to turn the array into a list of arguments.

printf "%s %s", *@params

The unary asterisk is often called the "unary unarray" operator because it has this effect on an array.

--

Nick Pavey
nick@skatingloons.com

irb(main):015:0> printf "%s %s", *@params[1..-1]
bar stuff=> nil

ain't it cool? :slight_smile:
-Mat

···

On Dec 27, 2006, at 3:17 PM, Jason Mayer wrote:

Neat! How would you use that to return "bar stuff" with the following?

@params = ["foo", "bar", "stuff"]
print @params.inspect
printf "%s %s", *@params

I guess you'll have to select the elements from the array.

AFAIK, the Array#* in only used to pack and unpack arrays into
argument lists (and other kinds of lists).
For example:

def multiple_returns
  1, 2, 3, 4
end

a = multiple_returns # a=1
a, b = multiple_returns # a=1, b = 2
a, *b = multiple_returns # a=1, b = [2,3,4]

Hope you'll get less n00b =P

cheers!

···

On 12/27/06, Jason Mayer <slamboy@gmail.com> wrote:

On 12/27/06, Timothy Hunter <TimHunter@nc.rr.com> wrote:
>
> Welcome to Ruby!
>
> Use the "*" (splat) operator to turn the array into a list of arguments.
>
> printf "%s %s", *@params
>
> The unary asterisk is often called the "unary unarray" operator because
> it has this effect on an array.

Neat! How would you use that to return "bar stuff" with the following?

@params = ["foo", "bar", "stuff"]
print @params.inspect
printf "%s %s", *@params

I'm not sure that Ri Array#* is quite what I meant to look for.

Jason Mayer wrote:

Welcoingme to Ruby!

Use the "*" (splat) operator to turn the array into a list of arguments.

printf "%s %s", *@params

The unary asterisk is often called the "unary unarray" operator because
it has this effect on an array.

Neat! How would you use that to return "bar stuff" with the following?

@params = ["foo", "bar", "stuff"]
print @params.inspect
printf "%s %s", *@params

You can't. That's not what "*" is for. The "unary unarray" operator is primarily used for exactly the purpose you described, variable-length argument lists. It has uses in other contexts, too, but they're outside the scope of your question.

If you want just some of the elements in the Array then you can use the method and specify which elements you're interested in. In your example, @params[1,2] ("2 elements starting at element 1") would do the trick.

I'm not sure that Ri Array#* is quite what I meant to look for.

You're right. In this context "*" is a unary operator. Array#* is a method.

···

On 12/27/06, Timothy Hunter <TimHunter@nc.rr.com> wrote:

The message was sent as multipart/alternative. The Gateway does not currently support this, as explained at:

If you want to change this, see:

and:

You can of course read Nick's message in the mailing list archives:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/231436

Hope this helps.

James Edward Gray II

···

On Dec 27, 2006, at 4:35 PM, Robert Klemme wrote:

On 27.12.2006 21:03, Timothy Hunter wrote:

Nick Pavey wrote:

Hi Folks,

...

I seem to be missing Nick's posting on the news side. I can see it on the mailing list. Is this the gateway again or my news provider?

Hi --

···

On Thu, 28 Dec 2006, Paulo Köch wrote:

I guess you'll have to select the elements from the array.

AFAIK, the Array#* in only used to pack and unpack arrays into
argument lists (and other kinds of lists).

Keep in mind that the unary * operator is different from the Array#*
method, which multiplies arrays:

   [1,2,3] * 2 => [1,2,3,1,2,3]

David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
    (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

James, thanks for the heads up! I guess you looked at the message and there was no HTML involved. But before I suggest a change of the gateway code I'll have to take a look. At the moment I'm imagining that extracting the first part with "text/plain" would work - at least for this posting. :slight_smile: But I can also see how that would increase the processing needed per each email...

Kind regards

  robert

···

On 27.12.2006 23:50, James Edward Gray II wrote:

On Dec 27, 2006, at 4:35 PM, Robert Klemme wrote:

On 27.12.2006 21:03, Timothy Hunter wrote:

Nick Pavey wrote:

Hi Folks,

...

I seem to be missing Nick's posting on the news side. I can see it on the mailing list. Is this the gateway again or my news provider?

The message was sent as multipart/alternative. The Gateway does not currently support this, as explained at:

Gray Soft / Not Found

If you want to change this, see:

Gray Soft / Not Found

and:

Gray Soft / Not Found

You can of course read Nick's message in the mailing list archives:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/231436

Hope this helps.

James Edward Gray II