elegant way to say "try this thing, one at a time, until conditionis met"

D

David Garamond

Example: I need to unmount /usr and /usr2, but currently I can't because
there are some opened files from those partitions. I am going to try
shutting down daemons or killing some processes (or other things), one
at a time, until I can unmount it.

def kill(process, signal)
...
end

# returns false if failed, true if succeeded or already unmounted
def unmount(partition)
...
end

unless unmount("/usr") && unmount("/usr2")
system "/etc/init.d/httpd stop"
system "/etc/init.d/squid stop"
unless unmount("/usr") && unmount("/usr2")
kill("rdiff-backup", :TERM)
sleep 3
unless unmount("/usr") && unmount("/usr2")
kill("rdiff-backup", :KILL)
sleep 3
...
...
end
end
end

I don't like this since I repeat unmount(). I can rewrite this as:

def cond_met
umount("/usr") && unmount("/usr2")
end

unless cond_met
...
unless cond_met
...
...
end
end

But still it's not pretty enough. Any more elegant way?
 
G

Gavin Sinclair

Lightly skimming the post, but answering the title, will these approaches do?

begin
...
end until condition

loop do
...
break if condition
...
end

catch:)done) do
...
throw :done if condition
...
end

I kind_of dislike the first solution aesthetically, but for simple cases
it's probably the best. The second and third solutions allow you to emit
a value from the block if that helps. In the third solution, you can
"throw" your way out of the block no matter how deeply you're nested, even
from another method.

Cheers,
Gavin
 
G

Gavin Sinclair

Lightly skimming the post, but answering the title, will these approaches do?

begin
...
end until condition

loop do
...
break if condition
...
end

catch:)done) do
...
throw :done if condition
...
end

I kind_of dislike the first solution aesthetically, but for simple cases
it's probably the best. The second and third solutions allow you to emit
a value from the block if that helps. In the third solution, you can
"throw" your way out of the block no matter how deeply you're nested, even
from another method.

Cheers,
Gavin
 
D

Dmitry V. Sabanin

Example: I need to unmount /usr and /usr2, but currently I can't because
there are some opened files from those partitions. I am going to try
shutting down daemons or killing some processes (or other things), one
at a time, until I can unmount it.
Sorry if that's not what you want, but i had this idea when I read your post:

# emulating condition
$counter = 0
def cond_met
$counter += 1
$counter > 1 ? true : false
end

# algo itself, services could be an array of commands to execute
services = %w| httpd squid sendmail |.reverse
begin
serv = services.pop
system "echo /etc/init.d/#{serv} stop"
end until cond_met
 
N

nobu.nokada

Hi,

At Mon, 31 May 2004 17:31:24 +0900,
David Garamond wrote in [ruby-talk:101855]:
Example: I need to unmount /usr and /usr2, but currently I can't because
there are some opened files from those partitions. I am going to try
shutting down daemons or killing some processes (or other things), one
at a time, until I can unmount it.

def kill(process, signal)
...
end

# returns false if failed, true if succeeded or already unmounted
def unmount(partition)
...
end

unless unmount("/usr") && unmount("/usr2")
system "/etc/init.d/httpd stop"
system "/etc/init.d/squid stop"
unless unmount("/usr") && unmount("/usr2")
kill("rdiff-backup", :TERM)
sleep 3
unless unmount("/usr") && unmount("/usr2")
kill("rdiff-backup", :KILL)
sleep 3
...
...
end
end
end

What about:

count = 0
until unmount("/usr") && unmount("/usr2")
case count += 1
when 1
system "/etc/init.d/httpd stop"
system "/etc/init.d/squid stop"
when 2
kill("rdiff-backup", :TERM)
sleep 3
when 3
kill("rdiff-backup", :KILL)
sleep 3
end
end

or:

stopper = [
proc {
system "/etc/init.d/httpd stop"
system "/etc/init.d/squid stop"
},
proc {
kill("rdiff-backup", :TERM)
sleep 3
},
proc {
kill("rdiff-backup", :KILL)
sleep 3
}
]
until unmount("/usr") && unmount("/usr2")
stopper.shift.call
end
 
G

Gregory Millam

Received: Mon, 31 May 2004 17:31:24 +0900
Example: I need to unmount /usr and /usr2, but currently I can't because
there are some opened files from those partitions. I am going to try
shutting down daemons or killing some processes (or other things), one
at a time, until I can unmount it.
...
But still it's not pretty enough. Any more elegant way?

Use a flow control that allows arbitrary exits. Such as returns and breaks

def cond_met?
umount("/usr") && umount("/usr2")
end

if it's a function:

def myfun

system '/etc/init.d/httpd stop'
system '/etc/init.d/squid stop'
return if cond_met?

system 'killall -INT motherinlaws'
return if cond_met?
... etc
end

else:

loop do

system '/etc/init.d/httpd stop'
break if cond_met?

system '/etc/init.d/squid stop'
break if cond_met?

system 'kill -9 pig ; /bin/fry pig'
break if cond_met?

break # This is needed so it doesn't actually loop :D
end

- Greg
 
P

Philipp Kern

$counter = 0
def cond_met
$counter += 1
$counter > 1 ? true : false
end

# algo itself, services could be an array of commands to execute
services = %w| httpd squid sendmail |.reverse
begin
serv = services.pop
system "echo /etc/init.d/#{serv} stop"
end until cond_met

What's the sense in stopping only two services listed in an array which has
more elements than the two?

Bye,
phil
 
D

David Garamond

count = 0
until unmount("/usr") && unmount("/usr2")
case count += 1
when 1
system "/etc/init.d/httpd stop"
system "/etc/init.d/squid stop"
when 2
kill("rdiff-backup", :TERM)
sleep 3
when 3
kill("rdiff-backup", :KILL)
sleep 3
end
end

Thanks to all. I like this one best :) The 1, 2, 3, ... clearly shows
the increasing step (from less to more extreme) which I would take to
meet the condition.
 
R

Robert Klemme

Dmitry V. Sabanin said:
Sorry if that's not what you want, but i had this idea when I read your post:

# emulating condition
$counter = 0
def cond_met
$counter += 1
$counter > 1 ? true : false
end

Just a sidenote: you don't have to convert the result of a boolean
expression to boolean by means of the ternary operator. This will do
exactly the same:

def cond_met
( $counter += 1 ) > 1
end

Regards

robert
 
G

Gregory Millam

Received: Mon, 31 May 2004 19:55:07 +0900
Thanks to all. I like this one best :) The 1, 2, 3, ... clearly shows
the increasing step (from less to more extreme) which I would take to
meet the condition.

By the way ...
If you're looking for processes that have files open on a mounted partition ... check the manual page for lsof

lsof # shows all open sockets (files, network, etc) used by programs.
lsof /usr # will show all programs that have open files (and what files they are) inside the filesystem of /usr

- Greg
 
D

David Garamond

Gregory said:
By the way ...
If you're looking for processes that have files open on a mounted partition ... check the manual page for lsof

lsof # shows all open sockets (files, network, etc) used by programs.
lsof /usr # will show all programs that have open files (and what files they are) inside the filesystem of /usr

Of course. Thanks. I tend to forget that too many times already :)
 
D

Dmitry V. Sabanin

Just a sidenote: you don't have to convert the result of a boolean
expression to boolean by means of the ternary operator. This will do
exactly the same:

def cond_met
( $counter += 1 ) > 1
end
Damn C background.. I always forget about this thing. Thanks, Robert
 
D

Dmitry V. Sabanin

What's the sense in stopping only two services listed in an array which has
more elements than the two?
It's just an emulation of how umount /usr will possibly work, probably it will unmount
partition before everything is killed. I should be more clear next time
 
J

Joel VanderWerf

David said:
unless unmount("/usr") && unmount("/usr2")
system "/etc/init.d/httpd stop"
system "/etc/init.d/squid stop"
unless unmount("/usr") && unmount("/usr2")
kill("rdiff-backup", :TERM)
sleep 3
unless unmount("/usr") && unmount("/usr2")
kill("rdiff-backup", :KILL)
sleep 3
...
...
end
end
end

Here's another way. I love how ruby lets you define control structures,
so you can make your code self-documenting:

require 'goal-attemptor'

GoalAttemptor.attempt_goal do
goal {unmount("/usr") && unmount("/usr2")}

attempt do
system "/etc/init.d/httpd stop"
system "/etc/init.d/squid stop"
end

attempt do
kill("rdiff-backup", :TERM)
sleep 3
end

attempt do
kill("rdiff-backup", :KILL)
sleep 3
end
end

------ goal-attemptor.rb ------

module GoalAttemptor
class Block
def initialize(attempt_goal_block)
instance_eval(&attempt_goal_block)

raise "no goal defined" unless @goal_block

until @goal_block.call
a = @attempt_blocks.shift
raise "nothing left to attempt" unless a
a.call
end
end

def goal(&goal_block)
@goal_block = goal_block
end

def attempt(&attempt_block)
(@attempt_blocks ||= []) << attempt_block
end
end

def attempt_goal(&attempt_goal_block)
Block.new(attempt_goal_block)
end
module_function :attempt_goal
end

if __FILE__ == $0

x = 2

include GoalAttemptor

attempt_goal do

goal {x > 5}

attempt do
x += 1
end

attempt do
x += 1
end

attempt do
x += 1
end

attempt do
x += 1
end

end

p x # ==> 6

end
 
R

Robert Klemme

Dmitry V. Sabanin said:
Damn C background.. I always forget about this thing. Thanks, Robert

It's superfluous to convert a boolean to a boolen in *all* programming
languages, although special bugs can arise from these conversions only in C
and other languages that don't have real booleans:

#define TRUE 1
#define FALSE 0

int x; /* a boolean */
....
if ( x == TRUE ) {
/* not always here if x is true! */
}

This should be valid C code that does the expected thing:

/* untested */
int cond_met() {
return ++counter > 1;
}

Regards

robert
 
M

Mikael Brockman

David Garamond said:
Example: I need to unmount /usr and /usr2, but currently I can't
because there are some opened files from those partitions. I am going
to try shutting down daemons or killing some processes (or other
things), one at a time, until I can unmount it.

[...]

But still it's not pretty enough. Any more elegant way?

This is how I'd do it.

| solutions = [
| proc do
| system "/etc/init.d/httpd stop"
| system "/etc/init.d/squit stop"
| end, proc do
| kill("rdiff-backup", :TERM)
| sleep 3
| end, proc do
| kill("rdiff-backup", :KILL)
| sleep 3
| end
| ]
|
| until unmount("/usr") && unmount("/usr2")
| solutions.shift.call
| end

Sorry about the eccentric formatting. For some reason, putting the
`end' and the `proc do' on different lines gives me the hibbie-jibbies.
 
D

David Garamond

Robert said:
It's superfluous to convert a boolean to a boolen in *all* programming
languages, although special bugs can arise from these conversions only in C
and other languages that don't have real booleans:

Agreed, it's generally wrong in most languages to use equality test for
booleans. I think this is because truth values transcend types (i.e. we
can ask for truth value for many types of values). Thankfully in Ruby
things are simplified: nil and false values are always false, everything
else is true.
 
R

Robert Klemme

David Garamond said:
Agreed, it's generally wrong in most languages to use equality test for
booleans. I think this is because truth values transcend types (i.e. we
can ask for truth value for many types of values).

No. The reason in C is, that there is a single value for 'false' but a
lot for 'true' and *not* that a lot of types can be used in a boolean
context (because they can be converted to bool aka int).

The same idiom is safe in Java, because it has boolean - but still
superfluous. In Java you should only use it in the rare case where you
want to compare two boolean expressions:

if ( getBoolX() == getBoolY() ) {...

is simpler and more intuitive than

if ( ( getBoolX() && getBoolY() ) || ( !getBoolX() && !getBoolY() ) ) {
....

Apart from that, there is a minor reason: the performance overhead for
some extra operations. But the main reason remains that it's superfluous
and extremely error prone. Whenever you see something like this
(regardless of programming language), your alarm bells should ring:

if x > 10
return true
else
return false

return x < 22 ? false : true

return x == true ? false : true

(Safe replacements left as an exercise for the reader. :))
Thankfully in Ruby
things are simplified: nil and false values are always false, everything
else is true.

From a certain point of view you could say that Ruby is more complicated
than C since it has two values meaning 'false' where C has only one. The
fact that all objects can occur in a boolean context just saves you some
explicit conversions you'd have to do in other languages.

Regards

robert
 
D

David Garamond

Alas, I actually didn't even think about C when writing this :) Was
comparing it to Python, PHP, Perl, etc. on my mind.
 
R

Robert Klemme

David Garamond said:
Alas, I actually didn't even think about C when writing this :) Was
comparing it to Python, PHP, Perl, etc. on my mind.

Ah, I see. Actually I was still referring to Dimitri's posting where he
talked about his "damn C background". I just wanted to make it very clear
that it's a bad idea to write such code *especially* in C. So C
background shouldn't be used as an excuse for this idiom. :)

Kind regards

robert
 

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,733
Messages
2,569,440
Members
44,830
Latest member
ZADIva7383

Latest Threads

Top