views:

70

answers:

5

I was wondering if there is a nice pattern or better yet a specific function which applies a function until there is no longer a change in it's result.

url = "http://www.zzz.com/yyy/lt%255B1%255D.jpg"
unescaped = unescape(url)
while unescaped != url do
  url = unescaped
  unescaped = unescape(unescaped)
end

Although the code above is basically ruby it is readable enough as psudocode I think. The first setting of unescaped is to http://www.zzz.com/yyy/lt%5B1%5D.jpg, the loop is then invoked as there has been a change and as such unescaped becomes http://www.zzz.com/yyy/lt[1].jpg, the loop is called again as there has yet again been a change but this time there us nothing to unescape and so url and unescaped become the same breaking the while loop.

There is nothing wrong with this code per-say I mealy wonder if there is a more sussinct way of representing it, either in psudocode form or, for my specific case, in ruby.

Much appreciate the help as always!

+1  A: 

You could write it as a recursive function that recurses until input and output match.

operate_while_change (input) {

  output <= change(input)

  output <= operate_while_change(output) if output != input

  return output

}
macabail
I like. Wonder if there is a nice way to implement it in ruby! Cheers for the heads up macabail!
roja
For ruby; recursive implementation :)def rec_unescape(url) url == (out = CGI::unescape(url)) ? out : rec_unescape(out)end
roja
+2  A: 

Based on macabail's answer to my original question here is a recursive solution for ruby:

def rec_unescape(url)
  url == (out = CGI::unescape(url)) ? out : rec_unescape(out)
end
roja
glad it helped you; that's definitely a tidy result there.
macabail
Both are recursive implementations. Yours is more concise, but his (which obviously calls itself in the body) is also recursive.
Myrddin Emrys
Heh, yes I know. Bad prose on my part I imagine!
roja
A: 

Turning macabil & roja's collaboration into a reusable ruby method:

#!/usr/bin/ruby1.8

module Kernel
  def while_changed
    result = :the_first_result_is_considered_a_change
    begin
      previous_result = result
      result = yield
    end until result == previous_result
  end
end

x = 0
while_changed do
  x += 1 if x < 4
  puts x
  x
end

# => 1
# => 2
# => 3
# => 4
# => 4
Wayne Conrad
A: 

Both answers above are nice. Here's a general function that does returns the first repeated output of a supplied block:

def stabilize(input)
  input == (result = yield(input)) ? result : stabilize(result)
end

fully_escaped = stabilize(url) {|s| CGI::unescape(s)}

Obviously, this function has no guard against blocks that never stabilize to a repeating result, but it does allow a simple way to pass just about any function, including unescaping.

Myrddin Emrys
Love it :)I may have a go at adding a "maximum depth" and "minimum stability" to this so that we can guard against the never-stabilising value and also those events where stability requires more than one stable iteration to verify true stability thus guarding against values which are artificially stable.
roja
A: 

If you wrap unescape() in a helper function, your own solution refactors out into something quite pretty, I think:

def unescape2(url)
    return url, unescape(url)
end

url = "http://www.zzz.com/yyy/lt%255B1%255D.jpg"
o_url, url = unescape2(url) until o_url == url

Or I guess you could use a lambda function:

url = "http://www.zzz.com/yyy/lt%255B1%255D.jpg"
o_url, url = lambda{|z| return z, unescape(z)} until o_url == url

Whichever you thought read better.

Shadowfirebird