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

Discussion in 'Ruby' started by David Garamond, May 31, 2004.

  1. 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?

    --
    dave
     
    David Garamond, May 31, 2004
    #1
    1. Advertising

  2. Re: elegant way to say 'try this thing, one at a time, until condition is met'

    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
     
    Gavin Sinclair, May 31, 2004
    #2
    1. Advertising

  3. Re: elegant way to say 'try this thing, one at a time, until condition is met'

    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
     
    Gavin Sinclair, May 31, 2004
    #3
  4. Re: elegant way to say "try this thing, one at a time, until condition is met"

    On Monday 31 May 2004 16:31, David Garamond wrote:
    > 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

    --
    sdmitry -=- Dmitry V. Sabanin
    MuraveyLabs.
    Spam Here ->
     
    Dmitry V. Sabanin, May 31, 2004
    #4
  5. David Garamond

    Guest

    Re: elegant way to say "try this thing, one at a time, until condition is met"

    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

    --
    Nobu Nakada
     
    , May 31, 2004
    #5
  6. Re: elegant way to say "try this thing, one at a time, untilcondition is met"

    Received: Mon, 31 May 2004 17:31:24 +0900
    And lo, David wrote:

    > 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
     
    Gregory Millam, May 31, 2004
    #6
  7. David Garamond

    Philipp Kern Guest

    Re: elegant way to say "try this thing, one at a time, until condition is met"

    On 2004-05-31, Dmitry V. Sabanin <> wrote:
    > $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
    --
    Please send replys (not followups) to the address set in Reply-To.
    Philipp Kern - PK2186-RIPE - http://www.philkern.de
     
    Philipp Kern, May 31, 2004
    #7
  8. wrote:
    > 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.

    --
    dave
     
    David Garamond, May 31, 2004
    #8
  9. Re: elegant way to say "try this thing, one at a time, until condition is met"

    "Dmitry V. Sabanin" <> schrieb im Newsbeitrag
    news:...
    > On Monday 31 May 2004 16:31, David Garamond wrote:
    > > 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


    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
     
    Robert Klemme, May 31, 2004
    #9
  10. Re: elegant way to say "try this thing, one at a time, untilcondition is met"

    Received: Mon, 31 May 2004 19:55:07 +0900
    And lo, David wrote:

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


    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
     
    Gregory Millam, May 31, 2004
    #10
  11. Gregory Millam wrote:
    > 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 :)

    --
    dave
     
    David Garamond, May 31, 2004
    #11
  12. Re: elegant way to say "try this thing, one at a time, until condition is met"

    On Monday 31 May 2004 20:08, Robert Klemme wrote:
    > "Dmitry V. Sabanin" <> schrieb im Newsbeitrag

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

    Damn C background.. I always forget about this thing. Thanks, Robert

    > Regards
    >
    > robert


    --
    sdmitry -=- Dmitry V. Sabanin
    MuraveyLabs.
    Spam Here ->
     
    Dmitry V. Sabanin, May 31, 2004
    #12
  13. Re: elegant way to say "try this thing, one at a time, until condition is met"

    On Monday 31 May 2004 18:53, Philipp Kern wrote:
    > On 2004-05-31, Dmitry V. Sabanin <> wrote:
    > > $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?

    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

    > Bye,
    > phil


    --
    sdmitry -=- Dmitry V. Sabanin
    MuraveyLabs.
    Spam Here ->
     
    Dmitry V. Sabanin, May 31, 2004
    #13
  14. David Garamond wrote:
    > 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
     
    Joel VanderWerf, May 31, 2004
    #14
  15. Re: elegant way to say "try this thing, one at a time, until condition is met"

    "Dmitry V. Sabanin" <> schrieb im Newsbeitrag
    news:...
    > On Monday 31 May 2004 20:08, Robert Klemme wrote:
    > > "Dmitry V. Sabanin" <> schrieb im Newsbeitrag

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

    > 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
     
    Robert Klemme, May 31, 2004
    #15
  16. Re: elegant way to say "try this thing, one at a time, until condition is met"

    David Garamond <lists@zara.6.isreserved.com> writes:

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

    --
    Mikael Brockman
     
    Mikael Brockman, Jun 1, 2004
    #16
  17. Robert Klemme wrote:
    > 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.

    > #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;
    > }


    --
    dave
     
    David Garamond, Jun 1, 2004
    #17
  18. Re: elegant way to say "try this thing, one at a time, until condition is met"

    "David Garamond" <lists@zara.6.isreserved.com> schrieb im Newsbeitrag
    news:40BC6E5F.1010502@zara.6.isreserved.com...
    > Robert Klemme wrote:
    > > 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).


    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
     
    Robert Klemme, Jun 1, 2004
    #18
  19. Robert Klemme wrote:
    >>Thankfully in Ruby
    >>things are simplified: nil and false values are always false, everything
    >>else is true.


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

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


    --
    dave
     
    David Garamond, Jun 1, 2004
    #19
  20. Re: elegant way to say "try this thing, one at a time, until condition is met"

    "David Garamond" <lists@zara.6.isreserved.com> schrieb im Newsbeitrag
    news:40BC7F27.1050209@zara.6.isreserved.com...
    > Robert Klemme wrote:
    > >>Thankfully in Ruby
    > >>things are simplified: nil and false values are always false,

    everything
    > >>else is true.

    >
    > 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
     
    Robert Klemme, Jun 1, 2004
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jan De Ceuster
    Replies:
    5
    Views:
    4,985
    Thomas Stanka
    Jan 13, 2005
  2. Daniel Klein

    Which way to say 'private'?

    Daniel Klein, Aug 21, 2003, in forum: Python
    Replies:
    5
    Views:
    510
    Jacek Generowicz
    Aug 22, 2003
  3. Replies:
    7
    Views:
    279
  4. matt neuburg

    ruby way to say this?

    matt neuburg, Oct 17, 2006, in forum: Ruby
    Replies:
    12
    Views:
    170
  5. Larry
    Replies:
    11
    Views:
    186
Loading...

Share This Page