2009-03-05 6 views
4

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

Так что я мог бы сделать:

a=['|','/','-','\\'] 
aNow=0 
# ... skip setup a big loop 
print a[aNow] 
aNow += 1 
aNow = 0 if aNow == a.length 
# ... do next step of process 
print "\b" 

Но я думал, что будет чище сделать:

def spinChar 
    a=['|','/','-','\\'] 
    a.cycle{|x| yield x} 
end 
# ... skip setup a big loop 
print spinChar 
# ... do next step of process 
print "\b" 

Конечно вызов spinChar хочет блок. Если я дам ему блок, он будет вешать бесконечно.

Как я могу получить только следующую информацию этого блока?

ответ

6

Руби yield не работает так, как ваш пример хотелось бы. Но это может быть хорошим местом для closure:

def spinner() 
    state = ['|','/','-','\\'] 
    return proc { state.push(state.shift)[0] } 
end 

spin = spinner 

# start working 
print spin.call 
# more work 
print spin.call 
# etc... 

На практике я думаю, что это решение может быть слишком «умный» для своего собственного блага, но понимание идеи Proc с может быть полезно в любом случае.

0

Ваш код немного наизнанку, если вы простите мне об этом. :)

Почему нет:

class Spinner 
    GLYPHS=['|','/','-','\\'] 
    def budge 
    print "#{GLYPHS[@idx = ((@idx || 0) + 1) % GLYPHS.length]}\b" 
    end 
end 

spinner = Spinner.new 
spinner.budge 
# do something 
spinner.budge 
spinner.budge 
# do something else 
spinner.budge 

Теперь, если вы хотите что-то вроде:

with_spinner do 
    # do my big task here 
end 

... тогда вам придется использовать многопоточность:

def with_spinner 
    t = Thread.new do 
    ['|','/','-','\\'].cycle { |c| print "#{c}\b" ; sleep(1) } 
    end 
    yield 
    Thread.kill(t) # nasty but effective 
end 
4

Я не думаю, что вы вполне понимаете, на yield делает в Ruby. Он не возвращает значение из блока - он передает значение в блок, который вы передали прилагаемому методу.

Я думаю, что вы хотите что-то более, как это:

def with_spinner 
    a=['|','/','-','\\'] 
    a.cycle do |x| 
    print x 
    $stdout.flush # needed because standard out is usually buffered 
    yield # this will call the do-block you pass to with_spinner 
    end 
end 

with_spinner do 
    #process here 
    #break when done 
end 
5

Мне нравятся все эти предложения, но я нашел генератор в стандартной библиотеке, и я думаю, что это больше вдоль линий того, что я хотел бы сделать: приращения

spinChar=Generator.new{ |g| 
    ['|','/','-','\\'].cycle{ |x| 
    g.yield x 
    } 
} 
#then 
spinChar.next 
#will indefinitly output the next character. 

Plain array index с модулем на замороженном массив кажется наиболее быстрым.

Нить Влада отличная, но не совсем то, что я хотел. И в spinner class приращение одна линии будет лучше, если рубин поддерживается i++ как GLYPHS[@i++%GLYPHS.length]

Макса spinner closure с нажимным сдвигом кажется немного интенсивной для меня, но в результате синтаксиса почти так же, как этот генератор. По крайней мере, я думаю, что это закрытие с проком.

Chuck's with_spinner на самом деле довольно близок к тому, что я хотел, но зачем ломаться, если вам не нужно с генератором, как указано выше.

Вадим, спасибо, что указал, что generator будет медленным.

"Here's a test of 50,000 spins:" 
        user  system  total  real 
"array index"  0.050000 0.000000 0.050000 ( 0.055520) 
"spinner class" 0.100000 0.010000 0.110000 ( 0.105594) 
"spinner closure" 0.080000 0.030000 0.110000 ( 0.116068) 
"with_spinner"  0.070000 0.030000 0.100000 ( 0.095915) 
"generator"  6.710000 0.320000 7.030000 ( 7.304569) 
+0

Генераторы болезненно медленны. Ruby 1.9 имеет волокна, которые могут делать примерно то же самое, но быстро. – vava

+0

http://stackoverflow.com/a/14894334/1698467 - какое название этого решения? 'spinner clos'? – skywinder

0

Когда-то я писал массив. Но это не просто массив, это массив, который имеет указатель, поэтому вы можете вызвать следующий foreverrr! http://gist.github.com/55955

Соедините этот класс с простым итератором или петлей, и вы являетесь золотым.

a = Collection.new(:a, :b, :c) 
1000.times do |i| 
    puts a.current 
    a.next 
end 
0

хе-хе, ответ выше мой все грязный.

a=['|','/','-','\\'] 
a << a 
a.each {|item| puts item} 
5

Я думаю, что вы на правильном пути с cycle. Как насчет чего-то вроде этого:

1.8.7 :001 > spinner = ['|','/','-','\\'].cycle 
=> #<Enumerable::Enumerator:0x7f111c165790> 
1.8.7 :002 > spinner.next 
=> "|" 
1.8.7 :003 > spinner.next 
=> "/" 
1.8.7 :004 > spinner.next 
=> "-" 
1.8.7 :005 > spinner.next 
=> "\\" 
1.8.7 :006 > spinner.next 
=> "|" 
Смежные вопросы