Выполнить остаток блока ruby ​​более одного раза - PullRequest
1 голос
/ 27 февраля 2009

Я делаю основу для определения процессов, которые могут включать выбор. У меня это работает, где каждый выбор - остров. Я бы предпочел, чтобы эти подпункты 'fork' были родительским выбором, чтобы все опции были правильно рассмотрены.

choose :one => lambda {
    choose [a, b]
    if a
      raise "Illegal"
    end
  },
  :two => ....

В настоящее время он всегда выбирает «а» (что само по себе выглядит лучше), но вызывает проблемы в дальнейшем. Действие: вариант с параметром 'b' никогда не рассматривается.

Я столкнулся с callcc (не переносимым для всех реализаций Ruby, из того, что я читал) и волокон (нововведением в 1.9, и нельзя предполагать, что он доступен), как вещами, которые можно убедить в работе Я не в восторге от двух реализаций или от чёрной магии любой из них, на самом деле.


В итоге я выбрал легкий путь и передал остаток вычисления в виде блока. Это стало немного менее болезненным, когда я увидел сходство с существующей структурой. Я просто надеюсь, что отступы не выходят за рамки.

Реальный случай значительно сложнее - есть побочные эффекты, но они содержатся в версионном хранилище значений ключей. Я также перечисляю все возможности и выбираю лучшую, чтобы она не могла просто остановиться на успехе.

Ответы [ 3 ]

1 голос
/ 27 февраля 2009

Назад, как и обещал.

Вот еще несколько идей:

  • Вы могли бы связать варианты выбора с выходом, чтобы выполнить обход по порядку перестановок. Другими словами, select может построить набор вложенных итераторов из переданных ему опций, и они просто уступят следующему в цепочке. Выход из закрытого блока возвращает вас сразу после выхода; если вам нужно больше (например, причины неудачи), вы можете поднять и спасти.
  • Причудливое расположение трех '(спасение, повышение и повторная попытка) может сделать это, опять же, с идеей выбора - вкладывать тела опций или встраивать их во вложенную структуру.
  • Если варианты дешевы и не имеют побочных эффектов, вам может понадобиться просто создать все перестановки и выполнить их итерацию.
  • Если они не свободны от побочных эффектов, вы можете попробовать какое-нибудь псевдомонадное решение, в котором вы лениво производите лямбды для каждой перестановки.
  • Более или менее эквивалентно (но отклоняясь от вашего первоначального вопроса) вы могли бы назначить им индекс (проще всего, если бы вы могли определить мощность каждого варианта, но в любом случае это возможно с сегментированным индексом) и выполнить итерацию через индексы.
  • Волокна были перенесены на 1.8.x

Но, учитывая все вышесказанное, я думаю, что ваш лучший ответ будет заключаться в том, чтобы обернуть нужную вам функциональность в классе или функции, реализовать ее с помощью callcc, а затем выполнить определение версии в или вокруг определения по мере необходимости, чтобы в правильной версии ruby ​​использовалась правильная реализация.

1 голос
/ 08 марта 2009

Как и просили, вот пример того, что я имел в виду, связывая выбор между доходами. Чистая реализация может выглядеть примерно так:

def choose_one_of_each(choices,results,&block)
    if choices.empty?
        yield results
      else
        c = choices.dup
        var,val = c.shift
        choose(val) { |v|
            choose_one_of_each(c,results.update(var => v),&block)
            }
      end
    end

def choose(options,&block)
    case options
      when Hash  then choose_one_of_each options,{},&block
      when Range then options.each { |item| yield item rescue nil }
      else            options.each { |item| yield item rescue nil }
      end
    end

И вы бы использовали его следующим образом (несколько расширенный из вашего примера, чтобы показать, как части взаимодействуют):

a = 7
b = 'frog'
choose(
    :one => [a,b], 
    :two => ['stay','go','punt'], 
    :three => {:how => ['in the car','in a boat','by magic'],:how_fast => 0..2 }
  ) do |choices|
     raise "illegal" if choices[:one] == a
     raise "You can't stay fast!" if choices[:two] == 'stay' and choices[:three][:how_fast] > 0
     raise "You go that slow!"    if choices[:two] == 'go'   and choices[:three][:how_fast] < 1
     print choices.inspect,"\n"
     end

Что произвело бы что-то вроде этого (из-за печати):

{:three=>{:how=>"in the car", :how_fast=>0}, :one=>"frog", :two=>"stay"}
{:three=>{:how=>"in the car", :how_fast=>0}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"in the car", :how_fast=>1}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"in the car", :how_fast=>1}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"in the car", :how_fast=>2}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"in the car", :how_fast=>2}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"in a boat", :how_fast=>0}, :one=>"frog", :two=>"stay"}
{:three=>{:how=>"in a boat", :how_fast=>0}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"in a boat", :how_fast=>1}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"in a boat", :how_fast=>1}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"in a boat", :how_fast=>2}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"in a boat", :how_fast=>2}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"by magic", :how_fast=>0}, :one=>"frog", :two=>"stay"}
{:three=>{:how=>"by magic", :how_fast=>0}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"by magic", :how_fast=>1}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"by magic", :how_fast=>1}, :one=>"frog", :two=>"punt"}
{:three=>{:how=>"by magic", :how_fast=>2}, :one=>"frog", :two=>"go"}
{:three=>{:how=>"by magic", :how_fast=>2}, :one=>"frog", :two=>"punt"}
1 голос
/ 27 февраля 2009

Возможно, вы захотите просмотреть решения [этой викторины] [1] для идей.

- MarkusQ

[1]: http://www.rubyquiz.com/quiz70.html"this викторина "

P.S. Я на пути к презентации, но я вернусь и предложу больше, когда вернусь, если никто не подошел к тарелке.

...