Learning Ruby, was a C geek

Hello all,

As I’m learning Ruby, I’ve decided to write a few programs in Ruby to get
a feel for the language.

I’ve been a C programmer for a long time. As such, I worry that I’m
thinking too much like a C programmer, and not like a Ruby programmer…

My first attempt has been an emulator for a cs-toy processor called IBCM
(itty-bitty computing machine). Its spec (only 5 pages) is available at:
http://www.cs.virginia.edu/~cs216/notes/ibcm-poo.pdf

I’ve written it, and it works correctly when compared to my program
written in C, but at its core is a large case-when-end statement.
This is not `sexy.’ I was wondering if there was a better way to do
this in Ruby, or if I’m just missing the point.

My code is at: http://manjac.ath.cx/nick/ruby-ibcm.rb

Thanks all,

···


Nicholas Paul Johnson
nickjohnsonSPAM^H^H^H^H@virginia.edu
http://manjac.ath.cx/nick
_
( ) ascii ribbon campaign - against html mail
X - against microsoft attachments
/ \ http://www.google.com/search?q=ascii+ribbon

As I’m learning Ruby, I’ve decided to write a few programs in
Ruby to get a feel for the language.

Welcome aboard!

I’ve been a C programmer for a long time. As such, I worry
that I’m thinking too much like a C programmer, and not like
a Ruby programmer…

You might be interested in a little project I’ve been working on -
translating the examples from Tim Jones’ “AI Application Programming” from C
into Ruby. Here’s an overview:

http://ai-app-prog.rubyforge.org/

and there are some notes on various translation pitfalls here:

http://ai-app-prog.rubyforge.org/#notes

Yours,

Tom

A few suggestions, if I may.

I wonder if your option detection might be more efficient as:

trace = true if ARGV.include? "-t"
dump = true if ARGV.include? "-d"
filenames = ARGV.delete_if { |n| n !~ /^[^-]/ }
fn = filenames.empty? ? nil : filenames.first

It would save you from looping through ARGV, just in case it’s a mile
long or something. :slight_smile:

Received: Sat, 3 Apr 2004 08:37:54 +0900
And lo, Nicholas wrote:

I’ve written it, and it works correctly when compared to my program
written in C, but at its core is a large case-when-end statement.
This is not `sexy.’ I was wondering if there was a better way to do
this in Ruby, or if I’m just missing the point.

This is a project similar in scope to an NES emulator I’ve written in C. When doing so, I tried and benchmarked 2 situations:

switch (opcode) {
case 0x01:
case 0x02 … (etc, for every opcode)
}

And:
typedef void (*operand)(address);
operand opcodes[0x100]; (You’d use 0x10)
op_0x01(int address) { … }
opcodes[0x01] = op_0x01.
Then call using: opcodes0x01;

Ruby caters to the second style rather well, adding in lambda blocks.

ops =
ops[0x01] = lambda { /* incrememnt accum 1 */ |address| Cpu.accum += 1 }

Either a hash or an array could do the job.

You’d really just need to fetch op[instruction byte 1 >> 4], if I’m reading that specs document correctly.

Hope that helps

  • Greg Millam

Nicholas Paul Johnson wrote:

Hello all,

As I’m learning Ruby, I’ve decided to write a few programs in Ruby to get
a feel for the language.

I’ve been a C programmer for a long time. As such, I worry that I’m
thinking too much like a C programmer, and not like a Ruby programmer…

I’ve written it, and it works correctly when compared to my program
written in C, but at its core is a large case-when-end statement.
This is not ‘sexy’.

Hi Nicholas,

Technically, case isn’t as sexy in Ruby as switch is in C, but I think
many Rubyists would use it the way you have done in this example.

The rest of your code suggests to me that you are on the correct path.

Welcome here !

daz

In article Pine.LNX.4.44.0404021817070.6456-100000@localhost.localdomain,

Hello all,

As I’m learning Ruby, I’ve decided to write a few programs in Ruby to get
a feel for the language.

I’ve been a C programmer for a long time. As such, I worry that I’m
thinking too much like a C programmer, and not like a Ruby programmer…

My first attempt has been an emulator for a cs-toy processor called IBCM
(itty-bitty computing machine). Its spec (only 5 pages) is available at:
http://www.cs.virginia.edu/~cs216/notes/ibcm-poo.pdf

I’ve written it, and it works correctly when compared to my program
written in C, but at its core is a large case-when-end statement.
This is not `sexy.’ I was wondering if there was a better way to do
this in Ruby, or if I’m just missing the point.

My code is at: http://manjac.ath.cx/nick/ruby-ibcm.rb

One thing I notice about your code that seems very C-ish is that you use
only integers as options for your case statements. Ruby’s case
statements are a lot more flexible than C’s switch. (Maybe you need to do
this because you’re reading & executing a binary file of IBCM code.) If
you’d rather work at a higher level of abstraction with assembly code you
could perhaps do something like:

case opcode
when :halt
#do halt stuff
when :and
#do and stuff
when …
end

Or perhaps you could even define a set of opcode classes that each
ecapsulate the bahavior of each opcode. Then instead of a case statement
you could just call a ‘execute’ (or whatever you want to call it) on each
statement that you read, so it would look something like:

class Machine
include Singleton
attr_accessor :accum
def initialize
@accum = 0
#define registers, states of the machine here
end
end

class Add
def initialize(value)
@value=value
@machine = Machine.instance
end
def execute
@machine.accum += value #scope of accum is an issue here, of course
end
def to_code #so you can convert the mnemonic to a code
5 #this gives you an assembler for free, but I’m not sure

end

end

def Add(value)
Add.new(value)
end

#…main loop

File.foreach(“IBCM.program”){ |line| #no more case statement
op = eval(line.strip)
op.execute #or if you want to convert to machine code: op.to_code
}

#contents of IBCM.program:

Load(0x55)
Add(2)
And(0xFF)
Xor(0x0F)
#…

So then your IBCM.program files are actually valid Ruby code.

…but then again, if you’ve got to read in binary data anyway,
maybe it wouldn’t make sense to do it this way.

Phil

···

Nicholas Paul Johnson nickjohnson@virginia.edu wrote:

Nicholas Paul Johnson wrote:

Hello all,

As I’m learning Ruby, I’ve decided to write a few programs in Ruby to get
a feel for the language.

I’ve been a C programmer for a long time. As such, I worry that I’m
thinking too much like a C programmer, and not like a Ruby programmer…

My first attempt has been an emulator for a cs-toy processor called IBCM
(itty-bitty computing machine). Its spec (only 5 pages) is available at:
http://www.cs.virginia.edu/~cs216/notes/ibcm-poo.pdf

I’ve written it, and it works correctly when compared to my program
written in C, but at its core is a large case-when-end statement.
This is not `sexy.’ I was wondering if there was a better way to do
this in Ruby, or if I’m just missing the point.

My code is at: http://manjac.ath.cx/nick/ruby-ibcm.rb

Thanks all,

I haven’t got any advice over what everyone else has suggested for the
layout of your program but I have spotted a couple of bugs.

Firstly the read word instruction doesn’t work. It either doesn’t take
any input or if you run the program with the trace option it gives an
error no such file or directory - -t

changing the line
accum = gets.to_i

to

accum = $stdin.gets.to_i

fixes this

The other problem is the JUMPL instruction jumps even if the acummalator
isn’t less than 0. The problem is in this line

progcount = off if accum & 0x8000

since 0 is a true value in Ruby the part accum & 0x8000 is always true.
You need to change it to

progcount = off if (accum & ox8000 != 0)

···


Mark Sparshatt

Another suggestion would be to remove the trace statements, which
clutter up the code. Instead use unit tests to ensure correctness.

Asim

···

On Sat, Apr 03, 2004 at 09:19:19AM +0900, Chris Dutton wrote:

A few suggestions, if I may.

I wonder if your option detection might be more efficient as:

trace = true if ARGV.include? “-t”
dump = true if ARGV.include? “-d”
filenames = ARGV.delete_if { |n| n !~ /[1]/ }
fn = filenames.empty? ? nil : filenames.first

It would save you from looping through ARGV, just in case it’s
a mile long or something. :slight_smile:


  1. ^- ↩︎

:slight_smile:

I’ve written it, and it works correctly when compared to my program
written in C, but at its core is a large case-when-end statement.
This is not `sexy.’ I was wondering if there was a better way to do
this in Ruby, or if I’m just missing the point.

Ruby caters to the second style rather well, adding in lambda blocks.

ops =
ops[0x01] = lambda { /* incrememnt accum 1 */ |address| Cpu.accum += 1 }

Yeah, thats cool.

While were on the subject, what is technically the difference between some
block, such as { |n| 1+n }, and the corresponding lambda { |n| 1+n }, or
more specifically, how does the interpreter treat them differently? Also,
could I assign a lambda to a variable as I would in scheme to create a
function?

···

On Sat, 3 Apr 2004, Gregory Millam wrote:


Nicholas Paul Johnson
nickjohnsonSPAM^H^H^H^H@virginia.edu
http://manjac.ath.cx/nick
_
( ) ascii ribbon campaign - against html mail
X - against microsoft attachments
/ \ ascii ribbon - Google Search

This is a project similar in scope to an NES emulator I’ve written in C. When doing so, I tried and benchmarked 2 situations:

switch (opcode) {
case 0x01:
case 0x02 … (etc, for every opcode)
}

And:
typedef void (*operand)(address);
operand opcodes[0x100]; (You’d use 0x10)
op_0x01(int address) { … }
opcodes[0x01] = op_0x01.
Then call using: opcodes0x01;

AARRGH! The suspense is killing me!

Which was faster?!!!

···

Received: Sat, 3 Apr 2004 15:26:46 +0900
And lo, Nicholas wrote:

While were on the subject, what is technically the difference between some
block, such as { |n| 1+n }, and the corresponding lambda { |n| 1+n }, or
more specifically, how does the interpreter treat them differently? Also,
could I assign a lambda to a variable as I would in scheme to create a
function?

They’re the same thing. The following two are identical

myproc = lambda { |n| 1+n }
mymethod(“5”,lambda)

mymethod(“5”) { |n| 1+n }

You could then call
myproc.call(“4”) #-> 5

So to continue with the opcode hash example
op[0x05] = lambda { |addr| … }
op[0x05].call(address)

the ‘lambda’ is needed to let ruby know it’s defining a Proc object.

c = { |n| 1+n } - doesn’t really make sense.

‘proc’ is an alias for lambda, but I believe it’s to be phased out because of proc/Proc confusin. and lambda is really just “Proc.new”

c = Proc.new { |n| 1+n }

These blocks, with ‘yield,’ are among my favorite things about ruby. =).

  • Greg

[…]

Could you please send mail using a valid email user/domainname so that
I and fellow users don’t receive all kinds of errors of choking MTAs/MDAs
because of the invalidity of using the underscore (_) in domainname
(im_not_giving_it_here@i_hate_spam.com).
At LEAST use a dash (-) instead of the underscore.

Thanks,

Paul

···

On Tue, Apr 06, 2004 at 02:44:19AM +0900, Asfand Yar Qazi wrote:


Student @ Eindhoven | JID: paul@luon.net
University of Technology, The Netherlands | email: paul@luon.net

Using the Power of Debian GNU/Linux <<< | GnuPG: finger paul@luon.net

lambda is almost the same thing as Proc.new. Consider this:

irb(main):001:0> l = lambda{ break }
=> #Proc:0x00372bf4@:1(irb)
irb(main):002:0> l.call
=> nil
irb(main):003:0> p = Proc.new { break }
=> #Proc:0x00366cdc@:3(irb)
irb(main):004:0> p.call
LocalJumpError: break from proc-closure
from (irb):3:in `call’
from (irb):4

Cheers,
Kent

···

On Apr 3, 2004, at 2:43 AM, Gregory Millam wrote:

‘proc’ is an alias for lambda, but I believe it’s to be phased out
because of proc/Proc confusin. and lambda is really just “Proc.new”

c = Proc.new { |n| 1+n }

These blocks, with ‘yield,’ are among my favorite things about ruby.
=).

  • Greg

Cheers,
Kent.

With regard to hating spam: I would recomend www.spamgourmet.com; I never
have to give out my email address if I don’t want to. Would also recomend
posting links to http://manjac.ath.cx/nick/emails.jsp in your webpages or
newsgroups, as it automatically generates faux email addresses and links
to confuse spam robots.

Asfand:
As for testing it out, which is faster: I’ve not tried, as it seems to me
that the case-when statement is more intuitively readable than the lambda
array. Either way, the simple program that I would try it in is not built
for benchmarking such things. I would encourage you to try it yourself.

···


Nicholas Paul Johnson
nickjohnsonSPAM^H^H^H^H@virginia.edu
http://manjac.ath.cx/nick
_
( ) ascii ribbon campaign - against html mail
X - against microsoft attachments
/ \ ascii ribbon - Google Search

On Tue, 6 Apr 2004, Paul van Tilburg wrote:

On Tue, Apr 06, 2004 at 02:44:19AM +0900, Asfand Yar Qazi wrote:
[…]

Could you please send mail using a valid email user/domainname so that
I and fellow users don’t receive all kinds of errors of choking MTAs/MDAs
because of the invalidity of using the underscore (_) in domainname
(im_not_giving_it_here@i_hate_spam.com).
At LEAST use a dash (-) instead of the underscore.

Thanks,

Paul

can you elaborate for those of us who are seeing double after a really long
week?

-a

···

On Sun, 4 Apr 2004, Kent S. wrote:

lambda is almost the same thing as Proc.new. Consider this:

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
URL :: Solar-Terrestrial Physics Data | NCEI
TRY :: for l in ruby perl;do $l -e “print "\x3a\x2d\x29\x0a"”;done
===============================================================================

Sure. Here’s what I’ve got by reading ruby.core:

  1. lambda{} is the same thing as Proc.new{}, except lambda has an arity
    checker plus it rescues ‘call’ method for LocalJumpError.

  2. proc{} is deprecated in favor of lambda{}.

  3. Proc.new without an associated block is defined like this:

    def method(&block)
    end

    equals

    def method
    block = Proc.new
    end

    and as I know, matz wants to deprecated this Proc.new usage as well.

Cheers,
Kent.

···

On Apr 3, 2004, at 1:24 PM, Ara.T.Howard wrote:

On Sun, 4 Apr 2004, Kent S. wrote:

lambda is almost the same thing as Proc.new. Consider this:

can you elaborate for those of us who are seeing double after a really
long
week?

Sure. Here’s what I’ve got by reading ruby.core:

  1. lambda{} is the same thing as Proc.new{}, except lambda has an arity
    checker plus it rescues ‘call’ method for LocalJumpError.

  2. proc{} is deprecated in favor of lambda{}.

  3. Proc.new without an associated block is defined like this:

    def method(&block)
    end

    equals

    def method
    block = Proc.new
    end

    and as I know, matz wants to deprecated this Proc.new usage as well.

Cheers,
Kent.

very cool - good to know.

-a

···

On Sun, 4 Apr 2004, Kent S. wrote:

On Apr 3, 2004, at 1:24 PM, Ara.T.Howard wrote:

On Sun, 4 Apr 2004, Kent S. wrote:

lambda is almost the same thing as Proc.new. Consider this:

can you elaborate for those of us who are seeing double after a really
long
week?

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
URL :: http://www.ngdc.noaa.gov/stp/
TRY :: for l in ruby perl;do $l -e “print "\x3a\x2d\x29\x0a"”;done
===============================================================================