Why does Tcl allow proc names with spaces, but not parameters with spaces? - arguments

Just for fun, I have written the following piece of code:
proc "bake a cake" {"number_of_people" {number_of_children}} {
set fmt "Take %d pounds of flour and %d bags of marshmallows."
puts [format $fmt "${number_of_people}" ${number_of_children}]
puts "Put them into the oven and hope for the best."
}
"bake a cake" 3 3
{bake a cake} 5 0
I found it funny that the proc names may include whitespace. I thought that combining this with essentially unused arguments would make it possible to make Tcl programs look very similar to spoken natural language, in a similar way Smalltalk does with its bakeACake forPeople: 3 andChildren: 3, just without the strange colon disturbing the sentences and the unnatural word order.
To explore this idea further, I tried the same pattern for the parameters of the proc, by replacing each _ with a simple space. The tclsh8.6 didn't like it:
too many fields in argument specifier "number of people"
(creating proc "bake a cake")
invoked from within
"proc "bake a cake" {"number of people" {number of children}} {
set fmt "Take %d pounds of flour and %d bags of marshmallows."
puts [format $fmt "${n..."
(file "bake.tcl" line 1)
This raised the following questions:
Is there a convincing reason as to why proc names may contain whitespace but parameter names cannot?
Is it just an implementation detail of proc?
Would it be possible to write spaceproc that allows this syntactic variant?

Have a close read at the proc documentation: each of the args in the arg list is itself a list, that must have 1 or 2 elements: the mandatory argument name and the optional default value. "number of people" has too many elements. You can get what you want with just another layer of braces:
% proc "bake a cake" {{"for people"} {"and children"}} {
puts "baking a cake for [set {for people}] people and [set {and children}] children"
}
% "bake a cake" 1 2
baking a cake for 1 people and 2 children
% "bake a cake"
wrong # args: should be "{bake a cake} {for people} {and children}"
I don't see the benefit of pursuing this experiment: the awkward variable names preclude the $ syntactic sugar.
Note that it's not that difficult to get Smalltalk-looking code
% proc bakeACake {forPeople: nPeople andChildren: nChildren} {
if {[set forPeople:] ne "forPeople:" || [set andChildren:] ne "andChildren:"} {
error {should be "bakeACake forPeople: nPeople andChildren: nChildren"}
}
puts "baking a cake for $nPeople people and $nChildren children"
}
% bakeACake
wrong # args: should be "bakeACake forPeople: nPeople andChildren: nChildren"
% bakeACake foo 1 bar 2
should be "bakeACake forPeople: nPeople andChildren: nChildren"
% bakeACake forPeople: 3 andChildren: 4
baking a cake for 3 people and 4 children
Although unlike Smalltalk, you can't have other commands starting with "bakeACake" (unless you dig into "namespace ensembles")

Related

yield is called once but gives two different results

Sorry for a silly question.
I'm reading a book to learn Ruby and there is a piece of code tha looks like that:
def make_casserole
puts "Preheat oven to 375 degrees"
ingredients = yield
puts "Place #{ingredients} in dish"
puts "Bake for 20 minutes"
end
make_casserole do
"noodles, celery, and tuna"
end
make_casserole do
"rice, broccoli, and chicken"
end
And the result you'll get:
Preheat oven to 375 degrees
Place noodles, celery, and tuna in dish
Bake for 20 minutes
Preheat oven to 375 degrees
Place rice, broccoli, and chicken in dish
Bake for 20 minutes
Question: Why the reslut looks as it is? They calling "yield" only once to get first one:
make_casserole do
"noodles, celery, and tuna"
end
How does it gets the second? Shouldn't result be like this:
Preheat oven to 375 degrees
Place noodles, celery, and tuna in dish
Bake for 20 minutes
Why Your Code Works As It Does
In Ruby, yield is a language keyword, but a number of objects also implement #yield or yield-like methods. Yield's job is to collaborate with a block. It might help to think of blocks as a sort of anonymous Proc object (which happens to have its own Proc#yield method) that is explicitly or implicitly available to every Ruby method as part of the language syntax.
The do/end keywords also delimit a block, so when you call:
make_casserole do
"noodles, celery, and tuna"
end
this is exactly the same as if you'd used a block-literal such as:
make_casserole { "noodles, celery, and tuna" }
You're then assigning the result of calling the block back to ingredients, and since each call to #make_casserole is returning only a single String, each call can only provide a single result.
Some Alternatives
While you could theoretically make a block yield multiple values, this isn't particularly idiomatic and is really more appropriate for positional arguments. Consider the following:
def make_casserole *ingredients
step = 0
instruction = "%d. %s\n"
puts [
"Preheat oven to 375 degrees",
"Place #{ingredients.join ', '} in dish",
"Bake for 20 minutes"
].map { format instruction, step += 1, _1 }
end
This does everything you currently do (and more), without needing a block at all. However, its behavior is largely hard-coded, which is where blocks can help.
When to Use Blocks
Blocks are most useful when you want to allow some unanticipated action to be defined or performed dynamically at runtime. For example, maybe you want to prepend an arbitrary word to each ingredient that you haven't predefined in your method or in the list of arguments you're passing:
# This method takes arguments, but yields each ingredient
# to the block for processing that wasn't defined by the
# current method.
def make_casserole *ingredients
puts "Preheat oven to 375 degrees"
puts ingredients.map { "Place #{yield _1} in dish" }
puts "Bake for 20 minutes"
end
# Call the method with some ingredients as arguments,
# and then transform the ingredients according to the
# rules defined in the block.
make_casserole('beans', 'rice') do
case _1
when /beans|barley/; "organic #{_1}"
when /rice|potatoes/; "brown #{_1}"
else _1
end
end
This could be done other ways, of course, but what it allows you to do is to defer decisions about how to act on the yielded items until runtime. The method doesn't really know (or care) what the block returns; it just yields each item in turn and lets the block act on the item. You might decide to pass a block that specifies substitutions, colors, levels of ripeness, or whatever else you want, all using the same method but using a different block.
By yielding each item to the block, the method's behavior can be modified on the fly based on the contents of the block. This creates a great deal of flexibility by leaving certain implementation details up to the block rather than the method.
yield yields control to a block. The return value of that block is what your method (in this case make_casserole) gets back from yield.
Consider if we have the block get input from the user:
irb(main):010:0> make_casserole do
irb(main):011:1* puts "Ingredients: "
irb(main):012:1> gets.strip
irb(main):013:1> end
Preheat oven to 375 degrees
Ingredients:
cheese and beans
Place cheese and beans in dish
Bake for 20 minutes
=> nil
The examples you've shown work the way they do because the block just returns a string without doing anything else. Understanding how blocks work is crucial to reading and writing idiomatic Ruby code.

How important is whitespace inside parens in Ruby?

I just discovered that whitespace inside parens can matter in Ruby in an unexpected way:
Here are 3 functions which look semantically identical to me:
def foo(x)
return {
:a => (x - 100),
}
end
def bar(x)
return {
:a => (x
- 100),
}
end
def zot(x)
return {
:a => (x -
100),
}
end
However, foo(10) and zot(10) return {:a=>-90} (as I expected) while bar(10) returns {:a=>-100} (to my dismay and disappointment).
What am I missing here?
It's an unusual case here but I believe what you're seeing is Ruby interpreting that as several consecutive statements and not a single statement. As in it sees that as:
x # Statement 1
-100 # Statement 2
Where the result of that block of code is -100.
In the case of zot you've expressed your intent to continue that line on the next by having a dangling - binary operator:
x - # Statement 1
100 # Statement 1 (cont)
It's worth noting that you can't do this when making method calls:
zot(x
-100 # Syntax error
)
As in that case the argument syntax rules are a lot more strict. Inside a free-form (...) structure you have considerably more latitude.
In Ruby, there are two possible expression separators: semicolon ; and newline.
So,
a
b
is the same as
a; b
Which means that
a
- b
is the same as
a; - b
which is the same as
a; b.-#()
Of course, you expected it to be equivalent to
a - b
which is the same as
a.-(b)
The point is: each of those two interpretations is equally valid. The language designer has to choose one of those two interpretations, and in this case, they chose the first one. Neither of the two is more "right" or more "correct".
If they had chosen the second interpretation, then you wouldn't have asked this question, but someone else would have asked the opposite one.

Need clarifying of exit and abort script Ruby

I am writing a little practice program with if and else. The code is as follows:
puts "What is your name?"
user_name = $stdin.gets.chomp
print "Hello #{user_name}! Welcome to Puzzles and Riddles v.1!"
puts "There are two doors \n 1. Riddles \n 2. Puzzles. \n Which door do you go through?"
answer_1 = $stdin.gets.chomp
if
answer_1 == "1"
puts "You have taken the Riddle room!"
print "Here is your riddle: \n You use a knife to slice my head and weep beside me when I am dead. \n What am I?"
end
riddle_1_answer = $stdin.gets.chomp
if
riddle_1_answer == ( riddle_1_answer == "An onion" ) || ( riddle_1_answer == "an onion" ) || ( riddle_1_answer == "Onion" ) || ( riddle_1_answer == "onion" )
puts "The correct answer is: An onion! \n You have advanced to round two."
else
puts "Sorry, your answer is incorrect. Think about it."
end
puts "Riddle 2. \n What has 4 fingers and a thumb, but is not living?"
riddle_2_answer = $stdin.gets.chomp
Now if the user got riddle_1_answer wrong how would I make it so that the program exits/aborts?
I tried adding exit(0) to the else section and it would terminate the program but would also come up with an error. So I'm not sure if the error is causing the program to end or its the exit(0) command
It's obvious from your question, the sample code, and your answers to #AndrewMarshall, that you need some guidance.
Here you go.
First, ruby is one of many programming languages, and there is a reason why many experienced programmers end up gravitating to it: ruby is object-oriented, expressive, powerful, and reasonably concise without being unnaturally terse. So, if you are going to learn ruby, learn by reading lots of good ruby code, from which you'll learn good practices.
Second, appearance matters because it impacts or enhances readability and comprehension. A good appearance lends to improved readability and more rapid comprehension. A bad appearance does the opposite.
The lack of alignment of the if, else, and end tokens in your code is bad; it made it hard to see the structure of the code's logic.
There are many rules of thumb in programming. Here are a few such rules that apply to most languages:
use alignment and indention properly
always think about "edge cases" (or errors)
limit and isolate complexity (use functions, modules, classes, & methods)
Don't Repeat Yourself (DRY)
So let's apply those two principles to your code and transform it a bit.
The first two lines:
puts "What is your name?"
user_name = $stdin.gets.chomp
What if the user enters CTRL-D (EOF)?
What if the user enters an empty line?
Is an error acceptable? An EOF on STDIN returns a nil, which causes an error on the chomp.
Is an empty string (zero length) name acceptable? If not, what should we do about it?
When there are complexities on doing something relatively simple, like getting a user name, encapsulate the complexities in a function or method, so the code needing the user name is not made confusing by the complexities of getting it.
Here's a replacement. First, let's manage the details (and complexities) of getting a user name within a small function.
def get_user_name
name = ''
while name.size == 0 do
print "What is your name? "
name = gets
exit(1) if name.nil? # exit program on EOF
name.strip!
end
name
end
Notice that we don't use chomp on name until after we've made sure that it isn't nil. Many programs react to EOF on input by exiting, aborting, or continuing on without any more questions. In this example, we'll just assume the user wants to quit.
Notice also that we used strip! instead of chomp!? That's because strip! will remove both leading and trailing whitespace, including the trailing newline.
Notice also that we didn't use $stdin.gets, but instead just gets? This is because the default object for gets is $stdin.
Probably a better approach to managing exceptional situations within small functions (methods), is to raise an exception, and let the higher level application logic decide how to manage it. With that in mind, here is a revised definition:
def get_user_name
name = ''
while name.size < 1 do
print "What is your name? "
name = gets
raise "End of input" if name.nil? # raise exception on EOF
name.strip!
end
name
end
Now, that get_user_name is defined, we can use it wherever we need a user name. We know that EOFs are managed, and we know that we won't get an empty string.
user_name = get_user_name
Now, let's do the rest of your original code, but aligned and indented properly.
print "Hello #{user_name}! Welcome to Puzzles and Riddles v.1!"
puts "There are two doors \n 1. Riddles \n 2. Puzzles. \n Which door do you go through?"
answer_1 = $stdin.gets.chomp
if answer_1 == "1"
puts "You have taken the Riddle room!"
print "Here is your riddle: \n You use a knife to slice my head and weep beside me when I am dead. \n What am I?"
end
riddle_1_answer = $stdin.gets.chomp
if riddle_1_answer == ( riddle_1_answer == "An onion" ) || ( riddle_1_answer == "an onion" ) || ( riddle_1_answer == "Onion" ) || ( riddle_1_answer == "onion" )
puts "The correct answer is: An onion! \n You have advanced to round two."
else
puts "Sorry, your answer is incorrect. Think about it."
end
puts "Riddle 2. \n What has 4 fingers and a thumb, but is not living?"
riddle_2_answer = $stdin.gets.chomp
Now that the alignment and indentation is correct, it's easier to see the logic, and its flaws. It's also easier to see a pattern of logic, and whenever you see a pattern, then DRY it up, and make methods (functions) to encapsulate the repetition.
But first, let's fix the obvious bugs.
The if expression is broken. Look at it again, and you'll see this:
if riddle_1_answer == TEST1 || TEST2 || TEST3 || TEST4
where I've used TESTn to replace the various case-sensitive tests you had.
This if expression will always fail because the value of riddle_1_answer will never be true or false and the result of the various TESTn expressions will always be true or false. I'm pretty sure you wanted this:
if TEST1 || TEST2 || TEST3 || TEST4
Second, when testing for a string value, it's not necessary to test all case variations. Just downcase the answer and test on lowercase test values (unless case-sensitivity is important). Alternatively, if simple character string tests aren't sufficient, then use a regular expression and use the i option for case-insensitive matching. Example:
if riddle_1_answer =~ /(?:an )?onion/i
Will test for "an onion" or "onion" in upper, lower, and mixed case.
Perhaps more important than these little errors, one should look to avoid repetition. The general pattern appears to be:
Ask a question
Accept an answer
Check the answer
Change the program state based on the answer
Repeat
When you see things like this, you should start thinking of arrays and hashes. Arrays are used when the values can be numerically indexed, and hashes are used when you want to get values associated with varying keys. Then, a simple loop can be used to iterate across the values of the array or hash.
So, seeing the pattern above, it becomes more clear that we will need a method to prompt for the question, get the answer, deal with a possible EOF and empty strings, validate non-empty answers, possibly repeating the question & answer when needed.
So let's define a little method to get an answer
# prompt_and_get_answer PROMPT, ANSWERS_DATA
#
# issue PROMPT, and get an answer, which must be one of the
# values in ANSWERS_DATA array, or one of the keys of the
# ANSWERS_DATA hash.
def prompt_and_get_answer prompt, answers_data
ans = ''
while ans.size < 1
print prompt
ans = $stdin.gets
if ans.nil?
raise "End of input"
end
ans.strip!
if answers_data.class == Hash # hash?
answers = answers_data.keys.sort
else
answers = answers_data.sort
end
matches = answers.grep(/#{ans}/i) # match possible valid answers
case matches.size # how many items found?
when 0
puts "#{ans} is not a valid answer. Use one of:"
puts answers.join(', ')
ans = ''
when 1 # return the match or the value of the matching key
ans = answers_data.class == Hash ? answers_data[matches[0]] : matches[0]
else
puts "#{ans} is ambiguous; be more specific to match one of:"
puts answers.join(', ')
ans = ''
end
end
ans
end
Now, we can use this method with a prompt as an argument, and get an answer, knowing that it's not going to be an empty answer and it's not going to be an EOF.
First, let's name the questions, and let's show the prompts as they would appear on output, without using the explicitly escaped newlines (\n).
$prompt1 = <<EOQ
There are two doors
1. Riddles
2. Puzzles
Which door do you go through?
EOQ
$answer1 = [ '1', '2' ]
$prompt2 = <<EOQ
Here is your riddle:
You use a knife to slice my head and weep beside me when I am dead.
What am I?
EOQ
$answer2 = [ 'onion' ]
ans1 = prompt_and_get_answer $prompt1, $answer1
if ans1 == '1'
do_riddles
elsif ans1 == '2'
do_puzzles
else
raise "Bad answer to prompt1"
end
def do_riddles
while true
ans = prompt_and_get_answer $prompt2, $answer2
if ans == 'onion'
puts "yay! you're right!"
break
else
puts "nope. Try again"
end
end
end
You can see from this that we've used methods (functions) to break the logic into smaller pieces, and to separate the details of getting answers from the details of testing them.
Later, we can see how to use tables and hashes to build up a list of questions.
Good luck.

Ruby remove the implicit break in Case statement? (How to make case like Switch)

x='bob'
case x
when "bob"
puts 'it stops here'
when 'bob'
puts 'but i want it to stop here'
end
Is there anyway to make case statements behave like the vanilla switch? So that it'll cycle through all the "when's" before breaking out? I'm surprised that ruby has it behave almost identically like a elsif.
Michael,
While your example is a bit misleading ('bob' matches both 'bob' and "bob" so the first case would always match), you just can use simple if's like in if_test method below :
def case_test(x)
puts case
when x > 3
"ct: #{x} is over 3"
when x > 4
"ct: #{x} is over 4"
end
end
case_test(4)
case_test(5)
def if_test(x)
puts "it: #{x} is over 3" if x > 3
puts "it: #{x} is over 4" if x > 4
end
if_test(4)
if_test(5)
This yields :
ct: 4 is over 3
ct: 5 is over 3
it: 4 is over 3
it: 5 is over 3
it: 5 is over 4
Note that you can also use multiple statements with when, which might help you or not depending on your real use case :
def many(x)
case x
when 'alice','bob'
puts "I know #{x}"
else·
puts "I don't know #{x}"
end
end
many('alice')
many('bob')
many('eve')
Yields :
I know alice
I know bob
I don't know eve
No. Case statements evaluate the first when block whose target's === method evaluates to true when passed the comparison, and stop there.

Ruby regex matching strings from an array?

I'm sort of new to regexs with Ruby, (or I suppose regex in general), but I was wondering if there was a pragmatic way to match a string using an array?
Let me explain, say I have a list of ingredients in this case:
1 1/3 cups all-purpose flour
2 teaspoons ground cinnamon
8 ounces shredded mozzarella cheese
Ultimately I need to split the ingredients into its respective "quantity and measurement" and "ingredient name", so like in the case of 2 teaspoons ground cinnamon, will be split into "8 ounces, and shredded mozzarella cheese.
So Instead of having a hugely long regex like: (cup\w*|teaspoon\w*ounce\w* ....... ), how can I use an array to hold those values outside the regex?
update
I did this (thanks cwninja):
# I think the all units should be just singular, then
# use ruby function to pluralize them.
units = [
'tablespoon',
'teaspoon',
'cup',
'can',
'quart',
'gallon',
'pinch',
'pound',
'pint',
'fluid ounce',
'ounce'
# ... shortened for brevity
]
joined_units = (units.collect{|u| u.pluralize} + units).join('|')
# There are actually many ingredients, so this is actually an iterator
# but for example sake we are going to just show one.
ingredient = "1 (10 ounce) can diced tomatoes and green chilies, undrained"
ingredient.split(/([\d\/\.\s]+(\([^)]+\))?)\s(#{joined_units})?\s?(.*)/i)
This gives me close to what I want, so I think this is the direction I want to go.
puts "measurement: #{arr[1]}"
puts "unit: #{arr[-2] if arr.size > 3}"
puts "title: #{arr[-1].strip}"
Personally I'd just build the regexp programmatically, you can do:
ingredients = [...]
recipe = Regexp.new(ingredients.join("|"), Regex::IGNORECASE)
or using union method:
recipe = Regexp.union(ingredients)
recipe = /#{regex}/i
… then use the recipe regexp.
As long as you save it and don't keep recreating it, it should be fairly efficient.
For an array a, something like this should work:
a.each do |line|
parts = /^([\d\s\.\/]+)\s+(\w+)\s+(.*)$/.match(line)
# Do something with parts[1 .. 3]
end
For example:
a = [
'1 1/3 cups all-purpose flour',
'2 teaspoons ground cinnamon',
'8 ounces shredded mozzarella cheese',
'1.5 liters brandy',
]
puts "amount\tunits\tingredient"
a.each do |line|
parts = /^([\d\s\.\/]+)\s+(\w+)\s+(.*)$/.match(line)
puts parts[1 .. 3].join("\t")
end

Resources