Any guides for good coding in Ruby?

J

James Edward Gray II

After I mentioned that, I realized it wasn't very clear.

No problem. It's a neat idea and I do want to play with it, I just
haven't been able to steal the time to sit down with it yet.
I intended to write some "back end" code to make it clearer how the
"front end"
worked -- and maybe I will do that soon. Soon as the mystical 25th
hour is added to the day. Well, maybe sooner.

Great. Whichever one of us gets to it first. ;)
FWIW, I wasn't thinking of anything graphical at all really -- not like
Conway's "Game of Life" or anything -- just numerical.

I was actually thinking of having little Ms and Fs walking around my
terminal. I just wanted to be able to watch what's going on.

I'm sure it could turn out interesting either way.

James Edward Gray II
 
J

Josef 'Jupp' Schugt

Arfin said:
1234 => "1,234.00"

I only handle the non-obvious "1234.00" -> "1,234.00" part of the task
but that one for float and integer regardless of length and sign. The
only minor trick is using positive lookahead to extract the right digits
in the case of a string that represents a float. Note that the argument
is left unchanged if

- it is no string
- it neither matches /^[-+]?\d+\.\d+$/ nor /^[-+]?\d+$/

The latter means that no modification is done if exponential notation is
used. This is done on purpose because one would not expect colons in
such a notation.

Extending the algorithm to numbers with leading zeroes is left as an
exercise to the reader :->

Here is the HOW IT IS DONE:

def threepack(x)
return x unless x.class == String
if /^[-+]?\d+\.\d+$/.match(x)
match = /((\d\d\d)+)(?=\.)/.match(x)
elsif /^[-+]?\d+$/.match(x)
match = /((\d\d\d)+)$/.match(x)
end
return x if match.nil?
result = match.pre_match
temp = match[1]
until temp.empty?
result << ',' unless ['', '-', '+'].member?(result)
result << temp[0..2]
temp = temp[3..-1]
end
result << match.post_match unless match.post_match.nil?
result
end

testdata = [

12345678,
'08/15',
'4711 Eau de Cologne',
'Beethoven',
'%d' % 1,
'%d' % 12,
'%d' % 123,
'%d' % 1_234,
'%d' % 12_345,
'%d' % 123_456,
'%d' % 1_234_567,

'%d' % -1,
'%d' % -12,
'%d' % -123,
'%d' % -1_234,
'%d' % -12_345,
'%d' % -123_456,
'%d' % -1_234_567,

'%4.2f' % 1.0,
'%5.2f' % 12.0,
'%6.2f' % 123.0,
'%7.2f' % 1_234.0,
'%8.2f' % 12_345.0,
'%9.2f' % 123_456.0,
'%10.2f' % 1_234_567.0,

'%5.2f' % -1.0,
'%6.2f' % -12.0,
'%7.2f' % -123.0,
'%8.2f' % -1_234.0,
'%9.2f' % -12_345.0,
'%10.2f' % -123_456.0,
'%11.2f' % -1_234_567.0,

]

testdata.each{|x| puts threepack(x)}

Here is the WHAT IS DONE:

12345678
08/15
4711 Eau de Cologne
Beethoven
1
12
123
1,234
12,345
123,456
1,234,567
-1
-12
-123
-1,234
-12,345
-123,456
-1,234,567
1.00
12.00
123.00
1,234.00
12,345.00
123,456.00
1,234,567.00
-1.00
-12.00
-123.00
-1,234.00
-12,345.00
-123,456.00
-1,234,567.00

Before someone asks: The examples were not chosen by chance; I live in
the former FRG capital of Bonn where Beethoven was born that is located
near Cologne.

Josef 'Jupp' Schugt
 
F

Florian Gross

Josef said:
Here is the HOW IT IS DONE:

def threepack(x)
return x unless x.class == String
if /^[-+]?\d+\.\d+$/.match(x)
match = /((\d\d\d)+)(?=\.)/.match(x)
elsif /^[-+]?\d+$/.match(x)
match = /((\d\d\d)+)$/.match(x)
end
return x if match.nil?
result = match.pre_match
temp = match[1]
until temp.empty?
result << ',' unless ['', '-', '+'].member?(result)
result << temp[0..2]
temp = temp[3..-1]
end
result << match.post_match unless match.post_match.nil?
result
end

Nifty code, though I wonder if it could be simplified by using
String#rindex? It's a nice method that's not commonly used, but very
nifty when needing to work from the end to the beginning.
 
C

Csaba Henk

I apologize. My intention was not to "bug you."

Uh, I'm sorry. I used "bugging me" just as a joke, I didn't really feel
"bugged" (see the smiley). I'd be happy to help you, but there are other
who could do that much more efficiently than me. My knowledge of text
manipulation ends at bashing strings with not too complicated regexps.

Csaba
 
J

James Edward Gray II

I'd be happy to help you, but there are other
who could do that much more efficiently than me. My knowledge of text
manipulation ends at bashing strings with not too complicated regexps.

Luckily, this knowledge isn't required for writing up a quiz idea.
See, it really is for everyone. ;)

James Edward Gray II
 
J

Josef 'Jupp' Schugt

Florian said:
Nifty code, though I wonder if it could be simplified by using
String#rindex?

I do not see any use of String#rindex that would simplify the code but
String#scan can do something for us:

def threepack(x)
return x unless x.class == String
if /^[-+]?\d+\.\d+$/.match(x)
match = /((\d\d\d)+)(?=\.)/.match(x)
elsif /^[-+]?\d+$/.match(x)
match = /((\d\d\d)+)$/.match(x)
end
return x if match.nil?
result = match.pre_match
result << ',' unless ['', '-', '+'].member?(result)
result << match[1].scan(/.../).join(',')
result << match.post_match unless match.post_match.nil?
result
end

An alternative solution using a complex Regexp is:

def threepack(x)
return x unless x.class == String
match = /^([-+]?)((\d{1,2})?)((\d{3})+)((\.\d+)?)$/.match(x)
return x if match.nil?
result = match[2].empty? ? match[1] : match[1] + match[2] + ','
result + match[4].scan(/.../).join(',') + match[6]
end

match[1] is the sign, match[2] is the digits before the three-digit
blocks, match[4] is the concatenation of all three-digit blocks,
match[6] is what follows the three-digit blocks. The strings are empty
if the given part is absent.

Josef 'Jupp' Schugt
 
A

Austin Ziegler

On Mar 22, 2005, at 3:09 PM, Arfin wrote:
I'm not aware of a core Ruby tool for commiying numbers, but it's
not hard to roll a solution:

It's not core Ruby, but Gavin Sinclair has added a number formatting
class that I wrote last year to his extensions project; this is one
of the most comprehensive number formatting classes you'll find,
IMO.

And I'm not biased at all ;)

-austin
 
A

Austin Ziegler

I disagree. I use 80 character width for everything. I have three
xterms spanned across my screen - all 80 characters wide. My IDEs
have the size of the window limited to 80 characters wide. It's
the perfect size. If a line goes too far then I know it's too
complicated (expect strings, string often span lines) or I'm
writing in Java. I think Java kills the 80 characters wides...
That's another story though.

I think everyone who doesn't use 80 characters (or less) should be
shot. But just in the foot, so they'll learn the error of their
ways. :)

I don't always use 80 characters. Most of the time? Yes. Other
times? No. Some of it is because of the vagaries of the Ruby
interpreter -- there are times when it is less readable to have the
line extend beyond the 80-character "limit", or it is simply not
possible (e.g., certain "raise Blah unless ..." type conditions).

I simply do what's pragmatic.

-austin
 
J

Josef 'Jupp' Schugt

Hi!

From Arfin's question on formatting numbers I conclude that there is a
need for a means to precisely control the display of numbers.

As long as (natural) science is concerned, printf serves its purpose
very well. What seemingly is missing is a powerful commercial number
formatting like the one of COBOL.

After a small archeological excavation campaign I used some COBOL book
to write a first draft of such a formatting. I use the COBOL notation
because it has shown its commercial strength for DECADES. Moreover I do
not know of any other equally powerful format string language for this
kind of application. The EBNF for the full formatting string still needs
to be written but that takes some time.

Currently planned features:

Fixnum#initialize(format, value)
Fixnum#to_s(format)
Fixnum#decimalPointIsComma
Fixnum#decimalPoint
Fixnum#groupingCharacter
Fixnum#currencySymbol

This is EBNF for Fixnum format.

PositiveInteger ::= '1' | '2' | '3' | '4' |
'5' | '6' | '7' | '8' | '9'.

Integer ::= '0' | PositiveInteger.

RepetitionFactor ::= '(' PositiveInteger [Integer+] ')'.

DecimalPointPlaceholder ::= 'v'.

SignPlaceholder ::= 's'.

DigitPlaceholder ::= '9'.

DigitsPlaceholder ::= Digitplaceholder+ |
Digitplaceholder RepetitionFactor.

UnsignedPlaceholder ::= DigitsPlaceholder
[
DecimalPointPlaceholder
DigitsPlaceholder
].

Fixnum ::= Sign Unsigned.

This is EBNF for Fixnum format string.

PositiveInteger ::= '1' | '2' | '3' | '4' |
'5' | '6' | '7' | '8' | '9'.

Integer ::= '0' | PositiveInteger.

RepetitionFactor ::= '(' PositiveInteger [Integer+] ')'.

ShowLeadingZero ::= '9'.
SuppressedLeadingZero ::= 'Z'.
StarLeadingZero ::= '*'.
DecimalPoint ::= '.'.
GroupingSymbol ::= ','.
NegativeSign ::= '-'.
AnySign ::= '+'.
Blank ::= 'B'.
Zero ::= '0'.
CurrencySymbol ::= '$'.
Credit ::= 'CR'.
Debit ::= 'DB'.
Slash ::= '/'.

'9', 'Z', and '*' are all used to display a digit. They only differn in
their handling of leading zeroes.

'9' displays any digit as is even if it is a leading zero

'Z' displays any digit as is with the exception of replacing a
leading zero by a blank character '.'

'*' displays any digit as if with the exception of replacing a
leading zero by an asterisk character '*'.

'.' displays a 'decimal point' (i.e. the character that separates the
integer and fractional part of a number). In many countries this
character is indeed a point '.' but in some (e.g. Germany) it is
','[1, 2, 5].

',' displays a grouping character (i.e. the character that is used to
group large numbers). In many countries people tend to use ',',
while in other ones '.' is preferred[1, 3, 4, 5].

'-' displays a minus sign '-' for negative numbers and a blank
character ' ' otherwise.

'+' displays a minus sign '-' for negative numbers and a plus sign
'+' otherwise.

'B' displays a blank character ' ' at the given position.
'0' displays a zero '0' at the given position

'$' displays a currency symbol[6] at the given position

'CR' displays 'CR' in the case of credit (i.e. a value smaller than
zero) and two blank characters otherwise.

'DB' displays 'DB' in the case of debit (i.e. a value not smaller than
zero) and two blank characters otherwise.

'/' displays a slash at the given position.

[1] The Boolean decimalPointIsComma property controls the defaults for
the decimalPoint[2] and groupingCharacter[3]. It defaults to

fixnum.decimalPointIsComma = false

[2] The actual character is defined by the decimalPoint property.
Default (comments show value of decimalPointIsComma):

fixnum.decimalPoint = '.' # false
fixnum.decimalPoint = ',' # true

The value must be a single character.

[3] The actual character is defined by the groupingCharacter property.
Defaults (comments show value of decimalPointIsComma):

fixnum.groupingCharacter = ',' # false
fixnum.groupingCharacter = '.' # true

[4] It is strongly recommended NEITHER to use the default setting NOR
'.' but a blank character:

fixnum.groupingCharacter = ' '

The value must be a single character.

[5] If an assignment sets decimalPoint and groupingCharacter to the
same character an ArgumentError will be risen.

[6] The currency symbol is defined by the currencySymbol property.
Default:

fixnum.currencySymbol = '$'

The currency symbol can be longer than one character. This allows
you to use ISO 4217 currency symbols if a given currency symbol is
inaccessible (you may e.g. wish to use 'EUR' instead of '€' for
euro, 'GBP' instead of '£' for british pound sterling, 'JPY'
instead of '¥' for japanese yen, and 'KRW' instead of '₩' for
korean won).

Comments?

Over and out,

Josef 'Jupp' Schugt
 
J

Jon A. Lambert

Josef said:
As long as (natural) science is concerned, printf serves its purpose
very well. What seemingly is missing is a powerful commercial number
formatting like the one of COBOL. ....
Comments?

This would make an excellent addition and possibly foundation for Ruby
report writing applications.

As a somewhat related aside, in my own little language, I implement a fixed
decimal type in addition to float and binary along the same lines of IBM's C
extension which was submitted to ANSI many years ago.

decimal(15,2) x;

which is equivalent to Cobol's

77 X PIC S(13)V99 COMP-3.

Fixed point decimal is actually present in all the 3xx architectures and has
proven to be very useful for financial applications. While this is not
architecturally optimal for arithmetic on x86, maybe there's some interest
in a fixed point decimal type as a class library?
 
J

Jim Freeze

* Jon A. Lambert said:
This would make an excellent addition and possibly foundation for Ruby
report writing applications.

As a somewhat related aside, in my own little language, I implement a fixed
decimal type in addition to float and binary along the same lines of IBM's
C extension which was submitted to ANSI many years ago.

decimal(15,2) x;

Hmm, could this be emulated with a wrapper around BigDecimal?
 
J

Jon A. Lambert

Jim said:
Hmm, could this be emulated with a wrapper around BigDecimal?

Certainly. It's only practical purpose would be to automatically keep
track of the decimal point during operations. That is I wasn't thinking of
actually storing and reading decimals in packed decimal or zoned decimal
format.
 
M

Michael Campbell

Looks good.

One small nit; "Debit" is usually indicated (counter-intuitively) by
"DR", not "DB" (though I have seen both.) No idea if there is a
standard for this, or what it says.
 
J

Josef 'Jupp' SCHUGT

At Mon, 25 Apr 2005 03:35:09 +0900,
Michael said:
One small nit; "Debit" is usually indicated (counter-intuitively) by
"DR", not "DB" (though I have seen both.) No idea if there is a
standard for this, or what it says.

According to my COBOL tutorial (I currently don't have a COBOL
compiler up and running) COBOL uses "DB".

After some careful analysis of the PIC strings I came to the
conclusion that they have too many shortcomings. I am presently
considering some "%xyz" notation that removes them. Only non-digits
are used so that prefixing xyz with a digit string can be used to
express repetition. I tried my best to make the formatting strings
mnemonic. It seems to make sense to allow for customization of what
'c' and 'd' are displayed as. Comments are welcome and essential:


1 digit ("_")
digit is displayed using "_". prefix controls zero.

1.1 blank zero ("_" or " _")
prefix " " displays zero as " ". default.

1.2 show zero ("!_")
prefix "!" displays zero as "0".

1.3 secure ("*_")
prefix "*" displays zero as '*'.

2 sign ("-")
sign is displayed using '-'. prefix controls non-negative sign.

2.1 blank non-negative sign (" -")
prefix " " displays non-negative sign as " ". default. prefix
controls zero's sign.

2.1.1 blank zero's sign (" -")
prefix " " displays zero's sign as " ". default.

2.1.2 positive zero's sign ("+ -")
prefix "+" displays zero's sign as "+".

2.1.3 negative zero's sign ("- -")
prefix "-" displays zero's sign as "-".

2.1.4. secure zero's sign ("* -")
prefix "*" displays zero's sign as "*".

2.2 show non-negative (sign ("+-")
prefix "+" displays non-negative sign as "+".

2.2.1 blank zero's sign
prefix " " displays zero's sign as " ".

2.2.2 positive zero's sign
prefix "+" displays zero's sign as "+". default.

2.2.3 negative zero's sign
prefix "-" displays zero's sign as "-".

2.2.4 secure zero's sign
prefix "*" displays zero's sign as '*'

2.3 secure non-negative sign ("*-")
prefix "*" displays non-negative sign as "*". prefix controls
display of zero's sign. note that prefix " " is prohibited
because it would contradict purpose of "*".

2.3.1 positive zero's sign ("+*-")
prefix "+" displays zero's sign as "+".

2.3.2 negative zero's sign ("-*-")
prefix "-" displays zero's sign as "-"

2.3.3 secure zero's sign ("**-")
prefix "*" displays zero's sign as "*". default.

2.4 shortcuts
default rules are such that shorthands exist for most likely
displays of sign (most likely is only displaying negative sign,
next is displaying zero's sign as if zero were positive).

2.4.1 "-" has same meaning as " -" and " -":
displays "-" if negative, ' ' otherwise

2.4.2 "+-" has same meaning as "++-":
displays "-" if negative, "+" otherwise.

2.4.3 "*-" has same meaning as "**-":
displays "-" if negative, "*" otherwise.

3 separator (",")
separator is displayed using ",". Prefix controls behavior if no
non-zero digits preceed.

3.1 blank separator (" ,")
displays " " in place of "," if no non-zero digits
preceede. default.

3.2 secure separator ("*,")
displays "*" in place of "," if no non-zero digits preceed.

4. decimal point (".")
decimal point is displayed using "."

5. currency symbol ("$")
currency symbol is displayed using "$".

6. credit ("c")
displays 'CR' in the case of credit (i.e. a value smaller than
zero). prefix controls display in case of non-credit.

6.1 blank credit (" c")
displays two blanks (" ") in case of non-credit

6.2 secure credit ("*c")
displays two asterisks ("**") in case of non-credit

6.3 zero is credit ("!c", "! c", "!*c")
zero is assumed to be credit. default is not to do this.

7. debit ("d")
displays 'DB' in the case of debit (i.e. a value not smaller than
zero). prefix controls display in case of non-debit

7.1 blank debit (" d")
displays two blanks (" ") in case of non-debit.

7.2 secure debit ("*d")
displays two asterisks ("**") in case of non-debit.

7.3 zero is debit ("!d", "! d", "!*d")
zero is assumed to be debit. default is not to do this.

Josef 'Jupp' Schugt
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top