Explain Ruby Commify large integer method
I found this snippet online and the purp开发者_开发百科ose is to commify any number including numbers with decimals ... 99999999 => 99,999,999. I can see that it uses regex but I am confused by "$1.reverse, $2"
def commify(n)
n.to_s =~ /([^\.]*)(\..*)?/
int, dec = $1.reverse, $2 ? $2 : ""
while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2,\3')
end
int.reverse + dec
end
Can anyone explain what is going on in this code?
$1
, $2
, $3
... are Perl legacy. They are capture group variables, that is, they capture the groups inside the regular expression.
A named group is indicated by parentheses. So, the first capture group matches ([^\.])
, which is any non dot character, and (\..*)
matches a dot character \.
and any other characters after it.
Note that the second group is optional, so in the line below you have the ternary expression $2 ? $2 : ""
, which is a crypty-ish way to get either the value of the capture of a blank string.
The int, dec = $1, $2_or_blank_string
is a parallel assignment. Ruby supports assigning more than one variable at once, it's not different than doing int = $1.reversed
then dec = $2
So int
now holds the integer part (reversed) and dec
the decimal part of the number. We are interested in the first one for now.
The next empty while
does a string substitution. The method gsub! replaces all occurences of the regular expression for the value in the seconf argument. But it returns nil
if no change happened, which ends the while
.
The /(,|\.|^)(\d{3})(\d)/
expression matches:
(,|\.|^)
A comma, a point or the beginning of the string(\d{3})
Three digits(\d)
A fourth digit
Then replaces it for \1\2,\3
. The \n
in a string substitution mean the nth capture group, just as the $n
variables do. So, it basically does: if I have four digits, just add a comma after the third one. Repeat until no group of four digits is found
Then, just reverse the integer part again and append the decimal part.
n.to_s =~ /([^\.]*)(\..*)?/
takes the number as a string and stores everything before the decimal point (or simply everything if there is no decimal point) in $1
and everything after and including it in $2
.
int, dec = $1.reverse, $2 ? $2 : ""
stores the reverse of $1
in int
and $2
, or ""
if $2
is nil
, in dec
. In other words int
now contains the part before the decimal point reversed and dec
contains the part after the point (not reversed).
The next line inserts a comma every three places into int
. So by reversing int
again we get the original integral part of the number with commas inserted every three places from the end. Now we add dec
again at the end and get the original number with commas at the right places.
Another way:
class Integer
def commify
self.to_s.gsub(/(\d)(?=(\d{3})+$)/,'\1,')
end
end
Then you can do 12345678.commify
and get the string 12,345,678
And here's one that handles floating point numbers:
class Float
def commify
self.to_s.reverse.gsub(/(\d\d\d)(?=\d)(?!\d*\.)/,'\1,').reverse
end
end
精彩评论