Printing why's (poignant) guide to ruby

B

Brian McCallister

I prefer ruby, but am not convinced it is "better for learning" than
python. Python and ruby are so similar linguistically that until you
get to the nuances (preferring internal iteration, passing ~functions
around is as common as in lisp, etc) they may as well be the same (to a
newcomer to programming).

If you have trouble explaining blocks, though, wait until you go for
list comprehensions!

Seriously, Python, Ruby, C, Java, Smalltalk, etc are all *great*
languages. The *best* learn-to-program language/environment is probably
Squeak, it is pretty much designed just for that, and oh yeah, is a
pretty good Smalltalk implementation too ;-)

-Brian
 
J

Joao Pedrosa

Hi,
Seriously, Python, Ruby, C, Java, Smalltalk, etc are all *great*
languages. The *best* learn-to-program language/environment is probably
Squeak, it is pretty much designed just for that, and oh yeah, is a
pretty good Smalltalk implementation too ;-)

Have you seen this interview by Alan Kay?
http://acmqueue.org/modules.php?name=Content&pa=showpage&pid=273

He says that Smalltalk has become more of a pro-tool and less of a
teaching environment since its beginnings.

Cheers,
Joao
 
S

Shashank Date

Joao said:
Have you seen this interview by Alan Kay?
http://acmqueue.org/modules.php?name=Content&pa=showpage&pid=273

He says that Smalltalk has become more of a pro-tool and less of a
teaching environment since its beginnings.

Yes, but at the same time, he devotes a lot of his time to support the
Etoys environment within Squeak which is meant for teaching young kids
how to author various Media (not just programming) on the computer. Take
it from me, I have done this at my son's school ( 6th and 7th grade) and
it was very successful. More about it here: http://www.squeakland.org

In my opinion Logo or Etoys is the best way to introduce programming to
kids. I tried Ruby a couple of times, but it did not work out quite like
the others. Of course, YMMV !
Cheers,
Joao

HTH,
-- shanko
 
F

Florian Gross

Ruth said:
Challenge: Well, how about explaining them (or at least blocks)?

If you attempt it, how about explaining blocks from the point of view of
an ex-Algol, Pascal, PL/1 programmer, for whom a block is just a way of
grouping several statements together so they can all be executed as a
result of some "test" (like if then else, while do, etc.). Is there
something extra or different about a Ruby block? What?

They can have arguments as well. Otherwise they are the general form of
the loop/if conditions/code that usually is done via special syntax.

An other way of looking at them is as anonymous functions that have
access to their definition scope plus a pretty syntax for it.

JavaScript has this without the pretty syntax:

[1, 2, 3].map(function(item) { return(item * 2) })

means the same as:

[1, 2, 3].map { |item| item * 2 }

yield() just calls the block that was associated with the current method
with the given arguments. This is needed because blocks are usually
passed anonymously into methods:

# Execute passed block twice and yields index
def two_times()
yield 0
yield 1
end

# Execute passed block twice and yields index as well
# This is implemented by forwarding the block to 2.times
def two_times(&block)
2.times(&block)
end

The &foo syntax assigns a block to a special variable in the form of a
Proc object meaning it will have .call() and .to_proc() methods and so
on in argument lists of method definitions. In argument lists of method
invocation it passes the value a variable refers to as the block of a
method.

On another note it might be interesting to have a look at concatenative
languages like Joy. These have something very similar to blocks as well:

[1 2 3] [2 *] map

What does that code to?

C:\dev.svn\ruby>irb -r joy
irb(main):001:0> joy = Joy.new
=> #<Joy:0x2b90d00 ...>
irb(main):002:0> joy.execute "[1 2 3] [2 *] map"; joy.stack
=> [[2, 4, 6]]

It takes a list and applies the [2 *] predicate to it yielding a new
list. Note that Joy is stack based and utilizes reverse polish notation
so 2 * pushes a 2 to the stack and multiplies the item that was on top
before with it.
 
R

Richard Dale

why said:
Nice. Well, it's not a great conversion. And I turned off the Table of
Contents, as it was paginating the sidebars. At any rate, the sidebars
are flowed into the main text.

But, hey, it's a PDF:

<http://rubyforge.org/frs/download.php/3073/why.s.poignant.guide-0.5.pdf>

_why
Thanks _why - just what I wanted! I look forward to trying it out teaching a
beginner programmer. I've downloaded it and it looks great - I've just
started my friend with looking at 'Thinking About [TLC] Logo' (from 1984)
as an intro anyway. Hmm, he says: 'they went on too many christian summer
camps' his first comment - I guess hackers all had beards in those days..

-- Richard
 
J

James G. Britt

Thanks Marcus, I have to admit some of the comments are making me lose
my cool here. The profanity, and accusations of trolling (with
simultaneous attacks on Python) as well as my unfortunate lack of
sleep are all adding up.

Please take into account that most folks here do not look down on
Python on Python developers. We just don't care for it, finding Ruby
to be a better personal choice. Though some would express their
tastes using stronger terms ...
Of course there are some very good points in this thread too. The
matter of teaching programming to a complete newbie is no trivial
matter either way.

I honestly think things like blocks are not a trivial matter.

Why:

5.times { print "hello " }

instead of:

5 times print hello

There is so much going on in '5.times { print "hello" }'. The more
experienced may not give a second thought, but to the enquiring newbie
it's a different matter.

Sure, but this is a poor example for teaching the use of a block.

It may make more sense to introduce newbies to blocks after showing
the creation of many methods that are essentially the same, save for
some bit of loop logic. Then explain that a block can be passed in
and used in place of that loop logic, eliminating the code redundancy.

It can be hard to devise good examples and demo scenarios. Most books
and tutorials give rather trivial examples, which makes it hard to get
a sense of why one would go to much trouble to learn something. Yet
if the examples are too hard, the user gets distracted by all the
expository scaffolding needed to set up the scenario.

Examples should both demonstrate some bit of syntax as well as provide
a reasonable motivation for learning this new syntax. I tend to
believe that when people see how they can abstract common behavior
among multiple methods and simplify their code using blocks, they get
interested enough to make the extra effort to really understand them.


James
 
W

William James

Ruth said:
Probably, but a = a + 1 was confusing for me when I first started
programming (30+ years ago) (solving for a gives a = 1/2 ;-)
....
Randy Kramer

Incorrect. Subtracting a from both sides leaves

0 = 1
 
A

Alexander Kellett

Please take into account that most folks here do not look down on
Python on Python developers. We just don't care for it, finding Ruby
to be a better personal choice. Though some would express their
tastes using stronger terms ...

i did note that it was a rant...

the problem is the defensive attitude and
bringing up of python in obviously non
related threads. its getting me a tad </sarcasm>
irritable.

Alex
 
A

Alexander Kellett

*putting on flame retardant pants*

;) hope they are comfortable :)
As a noob to Ruby who was convinced to try it after reading why's
wonderful work, i will say that i find Ruby "harder" than Python. To
call Python a "joke of a language" is pretty harsh by the way. It is
taking me much, much longer to work my way through the PickAxe book
than it did Learning Python. In just a few days of that book, I wrote
fully functional (albeit very tiny) applications that did what I
needed them to do. As well, at the time, the Python community was
helpful to the noob. Of course, Python doesn't have why, so it loses
on that aspect. :)

sorry for the harshness, it was aimed in a as good
as flame/troll like manner at the replied to poster...

i'm just bitter as the last time i had to suffer
in python's hands its indentation "magic" wasted me
most of an afternoon. i switched to ruby that same day
almost 4 years ago now, converted all my python code
to ruby code in the following few weeks, and settled
down in my new home.

maybe i should relax up on python.
i just hate that its brought up in ruby-talk.
wish people would stop that.

Alex
 
A

Alexander Kellett

It is not merely harsh; it is incorrect. Most people here prefer Ruby
to Python, but don't simply dismiss Python either.

/me notes that he's writing a ruby parser using python's pyggy library
its a nice and easy language, just that ruby's blocks ain't complex, new
programs follow easy rules ("stick {} around the block"), and list
comprehensions required an explanation for me. and i'm not exactly a
newbie programmer. ruby's blocks just clicked, no tutorial needed, i
grabbed the stdlib read the syntax files typed in a few examples from
programming ruby and haven't stopped reading ri since.

Alex
 
G

gabriele renzi

Florian Gross ha scritto:

On another note it might be interesting to have a look at concatenative
languages like Joy. These have something very similar to blocks as well:

[1 2 3] [2 *] map

What does that code to?

C:\dev.svn\ruby>irb -r joy
irb(main):001:0> joy = Joy.new
=> #<Joy:0x2b90d00 ...>
irb(main):002:0> joy.execute "[1 2 3] [2 *] map"; joy.stack
=> [[2, 4, 6]]

wait: you wrote a joy interpreter? that's cool :)
It takes a list and applies the [2 *] predicate to it yielding a new
list. Note that Joy is stack based and utilizes reverse polish notation
so 2 * pushes a 2 to the stack and multiplies the item that was on top
before with it.

I think even more mind boggling things relate to every function being
unary and to the implicit recursive operators :D
 
G

gabriele renzi

Navindra Umanee ha scritto:
I honestly think things like blocks are not a trivial matter.

Why:

5.times { print "hello " }

instead of:

5 times print hello

just my two cents:
maybe the point is starting withouth using blocks.
I found them hard too, when I initially started learning ruby , but I
could write cute programs (the usual web scraping, irc bots and such)
without using them. As of current ruby you can write net applications,
XMLRPC clients, access SOAP funny things withouth ever seing a block.
Then, you have an "ah-ha!" moment and you become happyer :)

Is not like explaining why in python:
fun=lambda a: print(a)
does not work, or what
foo.bar=fun.__get__(foo)
is supposed to do is /that/ obvious :)

there is always a learning curve, sadly.
 
G

gabriele renzi

Brian McCallister ha scritto:
If you have trouble explaining blocks, though, wait until you go for
list comprehensions!
^^^^^^^^^^^^^^^^^^^
more fun with generator expressions :D
 
F

Florian Gross

gabriele said:
On another note it might be interesting to have a look at
concatenative languages like Joy. These have something very similar to
blocks as well:

[1 2 3] [2 *] map

What does that code to?

C:\dev.svn\ruby>irb -r joy
irb(main):001:0> joy = Joy.new
=> #<Joy:0x2b90d00 ...>
irb(main):002:0> joy.execute "[1 2 3] [2 *] map"; joy.stack
=> [[2, 4, 6]]
wait: you wrote a joy interpreter? that's cool :)

Partially. See pseudo attachment.
It takes a list and applies the [2 *] predicate to it yielding a new
list. Note that Joy is stack based and utilizes reverse polish
notation so 2 * pushes a 2 to the stack and multiplies the item that
was on top before with it.


I think even more mind boggling things relate to every function being
unary and to the implicit recursive operators :D

Agreed, it's a very interesting language. :)
# See http://www.latrobe.edu.au/philosophy/phimvt/joy.html
# and http://factor.sourceforge.net/wiki/

require 'set'

class Joy
attr_reader :stack, :eek:ptions

Instructions = {
:false => lambda { |env| env.stack << false },
:true => lambda { |env| env.stack << true },
:maxint => lambda { |env| env.stack << env.options[:maxint] },
:setsize => lambda { |env| env.stack << env.options[:setsize] },
:stack => lambda { |env| env.stack << env.stack },
:conts => lambda { |env| callcc { |cc| env.stack << cc } }, # ?
:time => lambda { |env| env.stack << Time.now.to_i },
:rand => lambda { |env| env.stack << rand(env.options[:maxint]) },
:stdin => lambda { |env| env.stack << env.options[:stdin] },
:stdout => lambda { |env| env.stack << env.options[:stdout] },
:stderr => lambda { |env| env.stack << env.options[:stderr] },

:id => lambda { |env| },
:dup => lambda { |env| env.stack << env.stack.last },
:swap => lambda { |env| env.stack.insert(-2, env.stack.pop) },
:rollup => lambda { |env| env.stack.insert(-3, env.stack.pop) },
:rolldown => lambda { |env| env.stack << env.stack.slice!(-3) },
:rotate => lambda do |env|
env.stack[-1], env.stack[-3] = env.stack[-3], env.stack[-1]
end,
:popd => lambda { |env| ?? },
:dupd => lambda { |env| ?? },
:swapd => lambda { |env| ?? },
:rollupd => lambda { |env| ?? },
:rolldownd => lambda { |env| ?? },
:rotated => lambda { |env| ?? },
:pop => lambda { |env| env.stack.pop },
:choice => lambda do |env|
condi, theni, elsei = env.stack.slice!(-3 .. -1)
env.stack << (env.true?(condi) ? theni : elsei)
end,
:eek:r => lambda do |env|
env.stack << env.stack.pop | env.stack.pop
end,
:xor => lambda do |env|
env.stack << env.stack.pop ^ env.stack.pop
end,
:and => lambda do |env|
env.stack << env.stack.pop & env.stack.pop
end,
:not => lambda do |env|
# TODO: should be set complement for sets
env.stack << !env.stack.pop
end,
:+ => lambda { |env| env.stack << env.stack.pop + env.stack.pop },
:- => lambda { |env| env.stack << env.stack.pop - env.stack.pop },
:* => lambda { |env| env.stack << env.stack.pop * env.stack.pop },
:/ => lambda { |env| env.stack << env.stack.pop / env.stack.pop },
:rem => lambda { |env| env.stack << env.stack.pop % env.stack.pop },
:div => lambda { |env| env.stack += env.stack.pop.divmod(env.stack.pop) },
:sign => lambda { |env| env.stack << 0 <=> env.stack.pop },
:neg => lambda { |env| env.stack << -env.stack.pop },
:concat => lambda do |env|
x, y = env.stack.slice!(-2 .. -1)
env.stack << x + y
end,
:i => lambda do |env|
list = env.stack.pop
env.interpret_list list
end,
:map => lambda do |env|
list, handler = env.stack.slice!(-2 .. -1)
old_stack = env.stack.slice!(0 .. -1)
list.each do |item|
env.stack << item
env.interpret_list handler
end
env.stack.replace(old_stack + [env.stack.dup])
end
}

def true?(obj) obj end

def initialize(options = {}, stack = [], instructions = Instructions)
@options = {
:maxint => 2 ** 32,
:setsize => 2 ** 32,
:stdin => STDIN,
:stdout => STDOUT,
:stderr => STDERR
}.merge(options)
@stack, @instructions = stack, instructions
end

def string_unescape(string)
string.gsub(/\\(?:\d{1,3}|.)/) do |match|
escape = match[1 .. -1]
case escape
when "n" then "\n"
when "t" then "\t"
when "b" then "\b"
when "r" then "\r"
when "f" then "\f"
when /^\d+$/ then escape.to_i(8).chr
end
end
end

def parse(string)
string = string.dup
count = 0
accum = nil
states = [:code]

lex_re = /^[$#].+$ # Comment or shell command
|\(\*(?:\n|.)+?\*\) # Multi-line comment
|\s+ # White space
|-?\d[\d.\-\w]*\b # Numeric constant (lax)
|\b[A-Za-z]+[A-Za-z0-9=_\-]\b # Atomic symbol (word)
|==|.
/x

string.scan(lex_re) do |token|
p [states, accum, token] if $DEBUG

case states.last
when :code then
case token
when /^[$#]|^\(\*|^\s+$/ then
# Comments, shell commands and whitespace are all ignored for now.
when /^-?\d/ then
yield((Integer(token) rescue Float(token)))
when "[" then
states << :list
accum = ""
count = 1
when "{" then
states << :set
accum = ""
count = 1
when '"' then
states << :string
accum = ""
when "'" then
states << :char
else
yield token.intern
end

when :list, :set then
open = {:list => "[", :set => "{"}[states.last]
close = {:list => "]", :set => "}"}[states.last]
case token
when open then
accum << token
count += 1
when close then
count -= 1
if count != 0 then
accum << token
else
list = {:list => [], :set => Set.new}[states.last]
parse(accum) { |token| list << token }
yield list
states.pop
end
else
accum << token
end

when :string then
case token
when "\\" then
count = 1
when '"' then
if count == 0 then
yield string_unescape(accum)
states.pop
else
accum << token
count = 0
end
else
accum << token
count = 0
end
end
end
end

def interpret_token(token)
case token
when Numeric, Array, Set
@stack << token
when Symbol
@instructions[token].call(self)
end
end

def interpret_list(list)
list.each { |token| interpret_token token }
end

def execute(string)
parse(string) { |token| interpret_token token }
end
end
 
M

Mark Hubbart

Hi,

For someone who is new to programming
"string".display
wouldn't feel less natural than
print "string"

Really, think about that.

And why should we try to teach them the procedural paradigm and then
say 'Hey! forget all that stuff you learnt. It's not really a good way
to program. Now i'll teach you the object-oriented way" ?

No, you don't go paradigm-swapping on them. When it gets to a point
where it makes a difference, let them know that "puts" is the short
form of "STDOUT.puts". Tell them that STDOUT is an object that is
created for you, which can recieve messages that, among other things,
help you write to the screen. Have them write a little code using
STDOUT. Then tell them that the short form is easier, and more
commonly used.
Of course, all of this I say assuming that the goal is to create an
object-oriented programmer, not a procedural one.

I suspect the goal is to create a Ruby programmer :) Which means that
there will be some amount of overlapping.

cheers,
Mark
 
G

Gavri Fernandez

No, you don't go paradigm-swapping on them. When it gets to a point
where it makes a difference, let them know that "puts" is the short
form of "STDOUT.puts". Tell them that STDOUT is an object that is
created for you, which can recieve messages that, among other things,
help you write to the screen. Have them write a little code using
STDOUT. Then tell them that the short form is easier, and more
commonly used.

But why is this better than just starting with
STDOUT.puts
and later after object-oriented concepts have been completely
understood introduce
puts

why do you want to go this way?
puts -> STDOUT.puts -> puts

I really think doing it with hard-core object-orientation in the
beginning will help a lot. It's so natural.
I suspect the goal is to create a Ruby programmer :) Which means that
there will be some amount of overlapping.

Wasn't the goal just to create a programmer out of a non-programmer?
That was what this thread was about, right? Read all that i said above
with this assumption and you might agree with me :)
 
A

Alexander Kellett

But why is this better than just starting with
STDOUT.puts
and later after object-oriented concepts have been completely
understood introduce
puts

why do you want to go this way?
puts -> STDOUT.puts -> puts

I really think doing it with hard-core object-orientation in the
beginning will help a lot. It's so natural.

personally i'd teach via p
this is not only the inspection
output of a return value for irb,
but its also a heck of a lot less
confusing in some corner cases.

i doubt that any user will be
impressed by having to type
STDOUT.puts whenever they want
to see something :)

Alex
 
G

Gavri Fernandez

personally i'd teach via p
this is not only the inspection
output of a return value for irb,
but its also a heck of a lot less
confusing in some corner cases.

With irb for beginners, it's a lot more "natural". There's no need for
the P's...print, puts or otherwise :)
 

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
474,001
Messages
2,570,254
Members
46,850
Latest member
VMRKlaus8

Latest Threads

Top