Forum: Ruby some questions on language syntax

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Pavel S. (Guest)
on 2006-03-14 23:04
(Received via mailing list)
Hi, I'm new to this language and as I'm Perl user, some things seems
strange to me:

= %w{a b} produces ['a', 'b']. Is there some similarily easy way for
{'a' => 'b'}? Or, can I transform an array to some "list"? I can use
Hash['a', 'b'], but not Hash[%w{...}], because I cannot generate a list,
only an array.

= how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want
compare a and b accordind to some my own rules, i.e. if a[1] == b[2],
"return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.

= can I somehow make ruby produce warnings on 1 == '1' (number ==
string) like comparisons? In Perl true, in Ruby false. Many my mistakes
are of this kind and as these values seems same on output. ;-)

= why I can use {|...| ...} as argument for map, each etc., but I cannot
write foo = {|...| ...}, though I can write bar = [...] or bar = {...}?

Thanks,

P.
Bernhard 'elven' Stoeckner (Guest)
on 2006-03-14 23:15
(Received via mailing list)
Pavel S. scribbled on Tuesday 14 Mar 2006 21:50:

> Hi, I'm new to this language and as I'm Perl user, some things seems
> strange to me:
>
> = %w{a b} produces ['a', 'b']. Is there some similarily easy way for
> {'a' => 'b'}? Or, can I transform an array to some "list"? I can use
> Hash['a', 'b'], but not Hash[%w{...}], because I cannot generate a list,
> only an array.

You can, however, expand the array:
 Hash[*%w{a b c d}] yields {"a"=>"b", "c"=>"d"}

> = how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want
> compare a and b accordind to some my own rules, i.e. if a[1] == b[2],
> "return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.

irb(main):006:0> 0 == true
=> false

0 is not true :)

> = can I somehow make ruby produce warnings on 1 == '1' (number ==
> string) like comparisons? In Perl true, in Ruby false. Many my mistakes
> are of this kind and as these values seems same on output. ;-)

Not that I know of. Well, you could extend the == operator on Fixnum
(and
String) to throw a warning:

class Fixnum
  def ==(a)
    warn "Warn" unless a.is_a? Fixnum
    super(a)
  end
end

> = why I can use {|...| ...} as argument for map, each etc., but I cannot
> write foo = {|...| ...}, though I can write bar = [...] or bar = {...}?

You can't use {} literals in an assignment to denote a block because
Ruby
thinks it is supposed to be a Hash. Use Kernel#proc (or the alias
#lambda)
for that:

irb(main):026:0> p = proc {|n| puts n}
=> #<Proc:0xb7c98138@(irb):26>
irb(main):027:0> p.call(5)
5

You can pass that block to any method that wants a block (note the &
operator that denotes the passed argument is a block):

irb(main):029:0> [33, 44, 55].each &p
33
44
55

See also Method#block_given?

Hope that helps.
James G. (Guest)
on 2006-03-14 23:19
(Received via mailing list)
On Mar 14, 2006, at 3:03 PM, Pavel S. wrote:

> Hi, I'm new to this language and as I'm Perl user, some things
> seems strange to me:
>
> = %w{a b} produces ['a', 'b']. Is there some similarily easy way
> for {'a' => 'b'}? Or, can I transform an array to some "list"? I
> can use Hash['a', 'b'], but not Hash[%w{...}], because I cannot
> generate a list, only an array.

You are looking for the "splat" operator:

 >> Hash[*%w{a b}]
=> {"a"=>"b"}

> = how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want
> compare a and b accordind to some my own rules, i.e. if a[1] == b
> [2], "return" a[2] <=> b[2]? In Ruby this is not possible, because
> 0 is true.

We use sort_by() for that:

 >> %w{one two three}.sort_by { |str| [-str.length, str] }
=> ["three", "one", "two"]

> = can I somehow make ruby produce warnings on 1 == '1' (number ==
> string) like comparisons? In Perl true, in Ruby false. Many my
> mistakes are of this kind and as these values seems same on
> output. ;-)

Hmm, you could redefine ==(), but you don't want to do that, trust
me.  ;)  The transition phase will pass in time...

> = why I can use {|...| ...} as argument for map, each etc., but I
> cannot write foo = {|...| ...}, though I can write bar = [...] or
> bar = {...}?

You can use lambda() for this:

proc_object = lambda { |...| ... }

James Edward G. II
Jim W. (Guest)
on 2006-03-14 23:29
> On Mar 14, 2006, at 3:03 PM, Pavel S. wrote:
>> = how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want
>> compare a and b accordind to some my own rules, i.e. if a[1] == b
>> [2], "return" a[2] <=> b[2]? In Ruby this is not possible, because
>> 0 is true.
>
James G. wrote:
> We use sort_by() for that:

Or nonzero? ... eg.

    (a[1] <=> b[2]).nonzero? || (a[2] <=> b[2])

--
-- Jim W.
Gene T. (Guest)
on 2006-03-14 23:30
(Received via mailing list)
Pavel S. wrote:
> "return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.
>

use #sort_by like:
Enumerable#sort_by { |obj| obj.fld1, obj.fld2 }

this list give good, quick, *accurate* answers, no?
unknown (Guest)
on 2006-03-15 00:05
(Received via mailing list)
On Wed, 15 Mar 2006, Pavel S. wrote:

> = how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want compare a
> and b accordind to some my own rules, i.e. if a[1] == b[2], "return" a[2] <=>
> b[2]? In Ruby this is not possible, because 0 is true.

because in ruby you don't have too:

   a <=> b

when a and b are arrays just does that.  if you wanted to compare only
the
first elements (assuming there are more) you can simply

   a[0,2] <=> b[0,2]

or, if you a glutten for perlishment

   [a[1], a[2]] <=> [b[0], b[1]]


you can do __really__ compact sorting routines in ruby.  i wrote one
yesterday
that sorts strings like this

   /dmsp/nrt/data/incoming/afwa/2006.f13_0731556_DT.DAT
   /dmsp/nrt/data/incoming/afwa/2006.f13_0731737_DS.DAT

where

   2006.f13_0731556_DT.DAT
   ^^^^ ^^^ ^^^^^^^ ^^
   |    |   |       |
   year sat time    type

first by sat, then by year, then by time and finally by type

using only

   pats = %w[  f\d\d  ^\d{4}  _\d{7}  _\w{2}  ].map{|p|/#{p}/}

   basenames.sort!{|a,b| pats.map{|pat| a[pat]} <=> pats.map{|pat|
b[pat]} }

gotta love that!!

cheers.

-a
Pavel S. (Guest)
on 2006-03-15 00:23
(Received via mailing list)
Bernhard 'elven' Stoeckner wrote:
> Pavel S. scribbled on Tuesday 14 Mar 2006 21:50:
>>= %w{a b} produces ['a', 'b']. Is there some similarily easy way for
>>{'a' => 'b'}? Or, can I transform an array to some "list"? I can use
>>Hash['a', 'b'], but not Hash[%w{...}], because I cannot generate a list,
>>only an array.
>
> You can, however, expand the array:
>  Hash[*%w{a b c d}] yields {"a"=>"b", "c"=>"d"}

Oh, that's great!!! How could I not notice that? ;-)

>>= how can I do 'perlish' a[1] <=> b[1] || a[2] <=> b[2] if I want
>>compare a and b accordind to some my own rules, i.e. if a[1] == b[2],
>>"return" a[2] <=> b[2]? In Ruby this is not possible, because 0 is true.
>
> irb(main):006:0> 0 == true
> => false
>
> 0 is not true :)

Well, to be more precise, 0 is not equal to false (as is in Perl). :-)

So, how do you do a[1] <=> b[1] || a[2] <=> b[2]? Or, may be a.x <=> b.y
|| a.w <=> b.z would be better --- I don't want to suppose anything
about the internal structure of a and b now. However, Jim W.'s
(a[1] <=> b[2]).nonzero? || a[2] <=> b[2] seems OK (and it's even in
RDoc for Numeric#nonzero? ;-).

Gene T. wrote:
 > this list give good, quick, *accurate* answers, no?

;-) Must agree with this. Thank for all suggestions, they are very
valuable for me.

P.
Edward F. (Guest)
on 2006-03-15 00:30
(Received via mailing list)
On Wed, Mar 15, 2006 at 07:23:46AM +0900, Pavel S. wrote:
> However, Jim W.'s (a[1] <=> b[2]).nonzero? || a[2] <=> b[2] seems OK

I don't think that actually does what you want.  The test should
return -1 if a[1] is less than b[2], but instead it will return
"true".
Mike S. (Guest)
on 2006-03-15 00:36
(Received via mailing list)
On 14-Mar-06, at 5:28 PM, Edward F. wrote:

> On Wed, Mar 15, 2006 at 07:23:46AM +0900, Pavel S. wrote:
>> However, Jim W.'s (a[1] <=> b[2]).nonzero? || a[2] <=> b[2]
>> seems OK
>
> I don't think that actually does what you want.  The test should
> return -1 if a[1] is less than b[2], but instead it will return
> "true".
>

Are you sure?
------------------------------------------------------- Numeric#nonzero?
      num.nonzero?    => num or nil
------------------------------------------------------------------------
      Returns _num_ if _num_ is not zero, +nil+ otherwise. This behavior
      is useful when chaining comparisons:

         a = %w( z Bb bB bb BB a aA Aa AA A )
         b = a.sort {|a,b| (a.downcase <=> b.downcase).nonzero? || a
<=> b }
         b   #=> ["A", "a", "AA", "Aa", "aA", "BB", "Bb", "bB", "bb",
"z"]

Mike

--

Mike S. <removed_email_address@domain.invalid>
http://www.stok.ca/~mike/

The "`Stok' disclaimers" apply.
Pavel S. (Guest)
on 2006-03-15 00:45
(Received via mailing list)
removed_email_address@domain.invalid wrote:
>   year sat time    type
>
> first by sat, then by year, then by time and finally by type
>
> using only
>
>   pats = %w[  f\d\d  ^\d{4}  _\d{7}  _\w{2}  ].map{|p|/#{p}/}
>
>   basenames.sort!{|a,b| pats.map{|pat| a[pat]} <=> pats.map{|pat| b[pat]} }

Interesting idea, but may be _\w{2}\. would be more appropriate instead
of _\w{2} which can match also against a beginnig of the 'time' part. :)

P.
Jim W. (Guest)
on 2006-03-15 00:52
Edward F. wrote:
> On Wed, Mar 15, 2006 at 07:23:46AM +0900, Pavel S. wrote:
>> However, Jim W.'s (a[1] <=> b[2]).nonzero? || a[2] <=> b[2] seems OK
>
> I don't think that actually does what you want.  The test should
> return -1 if a[1] is less than b[2], but instead it will return
> "true".

No, it works:

> (0 <=> 1).nonzero?
=> -1

It was for this exact purpose that nonzero? was introduced.

BTW, I hope no one was confused by the typo ... the first b[2] should
have been a b[1].

-- Jim W.
unknown (Guest)
on 2006-03-15 01:03
(Received via mailing list)
On Wed, 15 Mar 2006, Pavel S. wrote:

>>   |    |   |       |
> Interesting idea, but may be _\w{2}\. would be more appropriate instead of
> _\w{2} which can match also against a beginnig of the 'time' part. :)
>
> P.

right you are!  bug number 63 squashed.

thanks!

-a
Mike S. (Guest)
on 2006-03-15 01:28
(Received via mailing list)
On 14-Mar-06, at 5:03 PM, removed_email_address@domain.invalid wrote:

>
>
>   ^^^^ ^^^ ^^^^^^^ ^^
> [pat]} }
>
> gotta love that!!

Tangentially speaking you can use unpack and sort_by to do this kind
of thing if it suits the situation (admittedly there's not a
sort_by!, but let's not let that get in the way :-)

sorted_basenames = basenames.sort_by { |name|
   name.unpack('A4 x A3 x A7 x A2').values_at(1, 0, 2, 3)
}

or the delightful

sorted_basenames = basenames.sort_by { |name| name.unpack('x5 A3 X8
A4 x5 A7 x A2') }

This is just an excuse to expose unpack and sort_by to people, not a
suggestion that it's necessarily appropriate or better in this case.

Mike

--

Mike S. <removed_email_address@domain.invalid>
http://www.stok.ca/~mike/

The "`Stok' disclaimers" apply.
unknown (Guest)
on 2006-03-15 01:32
(Received via mailing list)
On Wed, 15 Mar 2006, Mike S. wrote:

> sorted_basenames = basenames.sort_by { |name| name.unpack('x5 A3 X8 A4 x5 A7
> x A2') }
>
> This is just an excuse to expose unpack and sort_by to people, not a
> suggestion that it's necessarily appropriate or better in this case.

i actually like that.  whether regexes or pack codes are more obtuse is
definitely debatable - but that's pretty clean looking

thanks for the tip.

-a
This topic is locked and can not be replied to.