Learning Ruby, was a C geek...

  • Thread starter Nicholas Paul Johnson
  • Start date
N

Nicholas Paul Johnson

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^[email protected]
http://manjac.ath.cx/nick
_
( ) ascii ribbon campaign - against html mail
X - against microsoft attachments
/ \ http://www.google.com/search?q=ascii+ribbon
--
 
C

Chris Dutton

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. :)
 
A

Asim Jalis

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. :)

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


Asim
 
G

Gregory Millam

Received: Sat, 3 Apr 2004 08:37:54 +0900
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: opcodes[0x01](addr);

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
 
D

daz

Nicholas said:
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
 
G

Gregory Millam

Received: Sat, 3 Apr 2004 15:26:46 +0900
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
 
P

Phil Tomson

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
 
M

Mark Sparshatt

Nicholas said:
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)
 
K

Kent S.

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

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

Cheers,
Kent

'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.
 
A

Ara.T.Howard

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?

-a
--
===============================================================================
| 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
===============================================================================
 
K

Kent S.

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.
 
A

Ara.T.Howard

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
--
===============================================================================
| 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
===============================================================================
 
A

Asfand Yar Qazi

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: opcodes[0x01](addr);

AARRGH! The suspense is killing me!

Which was faster?!!!
 
P

Paul van Tilburg

]

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
[/QUOTE]
 

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

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top