Если кому-то интересно, я взломал версию, которая поддерживает остальную часть строки (хотя она может иметь ошибки, не слишком ее тестировала).
def text2int (textnum, numwords={}):
if not numwords:
units = [
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
"nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen",
]
tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]
scales = ["hundred", "thousand", "million", "billion", "trillion"]
numwords["and"] = (1, 0)
for idx, word in enumerate(units): numwords[word] = (1, idx)
for idx, word in enumerate(tens): numwords[word] = (1, idx * 10)
for idx, word in enumerate(scales): numwords[word] = (10 ** (idx * 3 or 2), 0)
ordinal_words = {'first':1, 'second':2, 'third':3, 'fifth':5, 'eighth':8, 'ninth':9, 'twelfth':12}
ordinal_endings = [('ieth', 'y'), ('th', '')]
textnum = textnum.replace('-', ' ')
current = result = 0
curstring = ""
onnumber = False
for word in textnum.split():
if word in ordinal_words:
scale, increment = (1, ordinal_words[word])
current = current * scale + increment
if scale > 100:
result += current
current = 0
onnumber = True
else:
for ending, replacement in ordinal_endings:
if word.endswith(ending):
word = "%s%s" % (word[:-len(ending)], replacement)
if word not in numwords:
if onnumber:
curstring += repr(result + current) + " "
curstring += word + " "
result = current = 0
onnumber = False
else:
scale, increment = numwords[word]
current = current * scale + increment
if scale > 100:
result += current
current = 0
onnumber = True
if onnumber:
curstring += repr(result + current)
return curstring
Пример:
>>> text2int("I want fifty five hot dogs for two hundred dollars.")
I want 55 hot dogs for 200 dollars.
Могут возникнуть проблемы, если у вас есть, скажем, «200 долларов». Но это было очень грубо.
Руби на самом деле имеет 4 очень похожих конструкции
Идея блоков - это своего рода способ реализации действительно легких моделей стратегии. Блок будет определять сопрограмму для функции, которой функция может делегировать управление с помощью ключевого слова yield. Мы используем блоки практически для всего в ruby, включая практически все циклические конструкции или в любом другом месте, которое вы использовали бы в using
в c #. Все, что находится за пределами блока, находится в области видимости для блока, однако обратное неверно, за исключением того, что return внутри блока возвращает внешнюю область видимости. Они выглядят так
def foo
yield 'called foo'
end
#usage
foo {|msg| puts msg} #idiomatic for one liners
foo do |msg| #idiomatic for multiline blocks
puts msg
end
Proc в основном берет блок и передает его в качестве параметра. Одним из чрезвычайно интересных применений этого является то, что вы можете передать процедуру в качестве замены блока в другом методе. В Ruby есть специальный символ для принудительного вызова proc, который является & amp ;, и специальное правило, согласно которому, если последний параметр в сигнатуре метода начинается с & amp;, это будет представление proc для блока для вызова метода. Наконец, есть встроенный метод под названием block_given?
, который будет возвращать true, если текущий метод имеет определенный блок. Похоже на это
def foo(&block)
return block
end
b = foo {puts 'hi'}
b.call # hi
Чтобы углубиться в это, есть действительно изящный трюк, который добавляет рельсы в Symbol (и был объединен с ядром ruby в 1.9). В основном это & amp; принуждение совершает свою магию, вызывая to_proc
все, что рядом. Поэтому ребята из rails добавили Symbol # to_proc, который будет вызывать себя для всего, что передается. Это позволяет вам написать действительно краткий код для любой функции стиля агрегации, которая просто вызывает метод для каждого объекта в списке
class Foo
def bar
'this is from bar'
end
end
list = [Foo.new, Foo.new, Foo.new]
list.map {|foo| foo.bar} # returns ['this is from bar', 'this is from bar', 'this is from bar']
list.map &:bar # returns _exactly_ the same thing
Более продвинутый материал, но imo, который действительно иллюстрирует вид магии, которую вы можете делать с проками.
Цель лямбды в рубине почти такая же, как и C #, способ создания встроенной функции для передачи или использования внутри. Подобно блокам и процедурам, лямбды являются замыканиями, но, в отличие от первых двух, они усиливают арность, и возврат из лямбды выходит из лямбды, а не из области видимости. Вы создаете его, передавая блок лямбда-методу или -> в ruby 1.9
l = lambda {|msg| puts msg} #ruby 1.8
l = -> {|msg| puts msg} #ruby 1.9
l.call('foo') # => foo
Только серьезные фанаты ruby действительно понимают это :) Метод - это способ превратить существующую функцию во что-то, что вы можете поместить в переменную. Вы получаете метод, вызывая функцию method
и передавая символ в качестве имени метода. Вы можете повторно связать метод, или вы можете привести его в процесс, если хотите похвастаться. Способ переписать предыдущий метод был бы
l = lambda &method(:puts)
l.call('foo')
. Здесь происходит то, что вы создаете метод для путов, приводите его в процесс, передавая его в качестве замены блока для лямбда-метод, который, в свою очередь, возвращает вам лямбду )
РЕДАКТИРОВАТЬ: для решения вопросов в комментариях
list.map & amp;: bar Могу ли я использовать этот синтаксис с блоком кода, который принимает более одного аргумента? Скажем, у меня есть hash = {0 => "hello", 1 => "world"}, и я хочу выбрать элементы с ключом в виде 0. Может быть, не хороший пример. - Брайан Шен
Здесь нужно немного углубиться, но чтобы действительно понять, как это работает, нужно понять, как работают вызовы метода ruby.
По сути, ruby не имеет концепции вызова метода, в результате объекты передают сообщения друг другу. Синтаксис obj.method arg
, который вы используете, на самом деле просто приукрашивает более явную форму, то есть obj.send :method, arg
, и функционально эквивалентен первому синтаксису. Это фундаментальная концепция языка, и именно поэтому такие вещи, как method_missing
и respond_to?
имеют смысл: в первом случае вы просто обрабатываете нераспознанное сообщение, во втором вы проверяете, прослушивает ли оно это сообщение. ,
Еще одна вещь, которую нужно знать, это довольно эзотерический оператор «сплат», *
. В зависимости от того, где он используется, он на самом деле делает очень разные вещи.
def foo(bar, *baz)
При вызове метода, если это последний параметр, splat заставит этот параметр объединить все дополнительные параметры, передаваемые в функцию (что-то вроде params
в C #)
obj.foo(bar, *[biz, baz])
При вызове метода (или чего-либо еще, который принимает списки аргументов), он превратит массив в пустой список аргументов. Фрагмент ниже эквивалентен фрагменту выше.
obj.foo(bar, biz, baz)
Теперь, имея в виду send
и *
, Symbol#to_proc
в основном реализовано так
class Symbol
def to_proc
Proc.new { |obj, *args| obj.send(self, *args) }
end
end
Итак, &:sym
собирается сделать новый процесс, что вызывает .send :sym
по первому аргументу, переданному ему. Если передаются какие-либо дополнительные аргументы, они объединяются в массив с именем args
, а затем разделяются на вызов метода send
.
Я заметил, что & amp; используется в трех местах: def foo (& amp; block), list.map & amp;: bar и l = lambda & amp; method (: put). Они имеют одинаковое значение? - Брайан Шен
Да, они делают. & Amp; позвоню to_proc
о том, что когда-либо рядом. В случае определения метода он имеет особое значение при работе с последним параметром, когда вы извлекаете сопрограмму, определенную как блок, и превращаете ее в процедуру. Определения методов на самом деле являются одной из самых сложных частей языка, существует огромное количество хитростей и специальных значений, которые могут быть в параметрах и размещении параметров.
b = {0 => "df", 1 => "kl"} p b.select {| key, value | key.zero? } Я попытался преобразовать это в p b.select & amp;: zero ?, но это не удалось. Я предполагаю, что это потому, что число параметров для блока кода равно двум, но & amp;: ноль? может принять только один параметр. Есть ли способ, которым я могу это сделать? - Брайан Шен
Это должно быть решено ранее, к сожалению, вы не можете сделать это с помощью этого трюка.
«Метод - это способ превратить существующую функцию во что-то, что вы можете поместить в переменную». почему l = method (: put) недостаточно? Что такое лямбда & amp; значит в этом контексте? - Брайан Шен
Этот пример был исключительно надуманным, я просто хотел показать эквивалентный код предыдущему примеру, где я передавал процесс методу lambda
. Я возьму немного времени позже и переписываю этот бит, но вы правы, method(:puts)
вполне достаточно. Я пытался показать, что вы можете использовать &method(:puts)
где угодно, чтобы взять блок. Лучшим примером будет этот
['hello', 'world'].each &method(:puts) # => hello\nworld
l = -> {| msg | ставит сообщение} #ruby 1.9: у меня это не работает. После того, как я проверил ответ Йорга, я думаю, что это должно быть l = -> (msg) {устанавливает msg}. Или, может быть, я использую неправильную версию Ruby? Мой - ruby 1.9.1p738 - Брайан Шен
Как я уже говорил в посте, у меня не было irb, когда я писал ответ, и вы правы, я обманываю это (потратить Подавляющее большинство моего времени в 1.8.7, поэтому я еще не привык к новому синтаксису)
Между битом Stabby и паренсом нет места. Попробуйте l = ->(msg) {puts msg}
. На самом деле этот синтаксис имел большое сопротивление, так как он сильно отличается от всего остального в языке.
Не совсем так. Но они очень похожи. Наиболее очевидное отличие состоит в том, что в C # лямбда-выражение может идти куда угодно, где у вас может быть значение, которое оказывается функцией; в Ruby у вас есть только один блок кода на вызов метода.
Они оба заимствовали идею из Lisp (язык программирования, датируемый концом 1950-х годов), который, в свою очередь, заимствовал лямбда-концепцию из Лямбда-исчисления Чёрча , изобретенного в 1930-х годах.
Являются ли эти два по существу одним и тем же? Они очень похожи на меня.
Они очень разные.
Прежде всего, лямбды в C # делают две очень разные вещи, только одна из которых имеет эквивалент в Ruby. (И этот эквивалент, сюрприз, лямбды, а не блоки.)
В C # литералы лямбда-выражения перегружены. (Интересно, что, насколько мне известно, это только перегруженные литералы ). И они перегружены типом результата . (Опять же, они являются только вещью в C #, которая может быть перегружена на тип результата, методы могут быть перегружены только на их типы аргумента.)
Литералы C # лямбда-выражения могут либо может быть анонимной частью исполняемого кода , либо абстрактным представлением анонимной части исполняемого кода, в зависимости от того, является ли их тип результата Func
/ Action
, или Expression
.
Ruby не имеет эквивалента для последней функциональности (ну, есть специфичные для интерпретатора непереносимые нестандартизированные расширения). И эквивалент для прежней функциональности - лямбда, а не блок.
Синтаксис Ruby для лямбды очень похож на C #:
->(x, y) { x + y } # Ruby
(x, y) => { return x + y; } // C#
В C # вы можете удалить return
, точку с запятой и фигурные скобки, если у вас есть только одно выражение в качестве body:
->(x, y) { x + y } # Ruby
(x, y) => x + y // C#
Вы можете опустить скобки, если у вас есть только один параметр:
-> x { x } # Ruby
x => x // C#
В Ruby вы можете опустить список параметров, если он пуст:
-> { 42 } # Ruby
() => 42 // C#
Альтернативой использованию литерального лямбда-синтаксиса в Ruby является передача аргумента блока методу Kernel#lambda
:
->(x, y) { x + y }
lambda {|x, y| x + y } # same thing
Основное различие между этими двумя заключается в том, что вы не Не знаю, что делает lambda
, поскольку он может быть переопределен, переписан, упакован или изменен иным образом, тогда как поведение литералов не может быть изменено в Ruby.
В Ruby 1.8 вы также можете использовать Kernel#proc
, хотя вам, вероятно, следует избегать этого, так как этот метод делает что-то другое в 1.9.
Другим отличием Ruby от C # является синтаксис для , который называет лямбда-выражением:
l.() # Ruby
l() // C#
Т.е. в C # вы используете тот же синтаксис для вызова лямбды, который вы использовали бы для вызова чего-либо еще, тогда как в Ruby синтаксис для вызова метода отличается от синтаксиса для вызова любого другого типа вызываемого объекта.
Другое отличие состоит в том, что в C # ()
встроен в язык и доступен только для определенных встроенных типов, таких как методы, делегаты, Action
s и Func
s, тогда как в Ruby .()
просто синтаксический сахар для .call()
и, таким образом, может работать с любым объектом , просто реализуя метод call
.
Итак, что же являются лямбдами? Ну, они экземпляры класса Proc
. За исключением небольшого усложнения: на самом деле есть два разных типа экземпляров класса Proc
, которые немного различаются. (ИМХО, класс Proc
следует разделить на два класса для двух разных типов объектов.)
В частности, не все Proc
являются лямбдами. Вы можете проверить, является ли Proc
лямбда-выражением, вызвав метод Proc#lambda?
. (Обычное соглашение заключается в том, чтобы называть лямбда-выражения Proc
s лямбда-выражениями, а не лямбда-выражения Proc
просто «процессами».) до Kernel#proc
. Тем не менее, обратите внимание, что до Ruby 1.9 Kernel#proc
создает лямбду , а не proc.
11148 В чем разница? По сути, лямбды ведут себя больше как методы, а проки - как блоки.
Если вы следили за некоторыми обсуждениями списков рассылки Project Lambda для Java 8, вы могли столкнуться с проблемой, заключающейся в том, что не совсем понятно, как нелокальный поток управления должен вести себя с лямбдами. В частности, есть три возможных разумных поведения для return
(ну, три возможных , но только два действительно имеют смысл ) в лямбда-выражении:
Этот последний немного ненадежен, так как в общем случае метод будет иметь уже возвращенный , но два других имеют смысл, и ни один не является более правильным или более очевидным, чем другой. Текущее состояние Project Lambda для Java 8 состоит в том, что они используют два разных ключевых слова (return
и yield
). Ruby использует два различных типа Proc
s:
Они также отличаются тем, как они обрабатывают привязку аргумента. Опять же, лямбды ведут себя больше как методы, а проки ведут себя больше как блоки:
nil
Array
(или отвечает на to_ary
), и процедура имеет несколько параметров, массив будет распакован, а элементы привязаны к параметрам (точно так же, как и в случае назначения деструктурирования) Блок - это, по сути, легковесный проц. Каждый метод в Ruby имеет ровно один блочный параметр , который фактически не отображается в списке параметров (подробнее об этом позже), то есть неявно. Это означает, что в каждый метод , вызывающий , вы можете передать аргумент блока, независимо от того, ожидает метод метод или нет.
Поскольку блок не отображается в списке параметров, нет имени, которое вы можете использовать для ссылки на него. Итак, как вы используете это? Ну, единственные две вещи, которые вы можете сделать (не совсем, но об этом позже), это вызвать неявно через ключевое слово yield
и проверить, был ли блок передан через block_given?
. (Поскольку имени нет, вы не можете использовать методы call
или nil?
. Как бы вы их назвали?)
Большинство реализаций Ruby реализуют блоки очень легковесным способом. В частности, они на самом деле не реализуют их как объекты. Однако, поскольку у них нет имени, вы не можете ссылаться на них, поэтому на самом деле невозможно определить, являются ли они объектами или нет. Вы можете просто думать о них как о процах, что облегчает эту задачу, поскольку есть одна менее важная концепция, о которой следует помнить. Просто учтите тот факт, что они на самом деле не реализованы как блоки, как оптимизацию компилятора.
to_proc
и &
В есть способ ссылки на блок: оператор &
сигил / модификатор / унарный префиксный оператор. Он может появляться только в списках параметров и списках аргументов.
В списке параметров это означает « заключить неявный блок в процесс и связать его с этим именем». В списке аргументов это означает « развернуть этот Proc
в блок».
def foo(&bar)
end
Внутри метода, bar
теперь привязан к объекту proc, который представляет блок. Это означает, например, что вы можете сохранить его в переменной экземпляра для последующего использования.
baz(&quux)
В этом случае baz
на самом деле является методом, который принимает нулевые аргументы. Но, конечно, он принимает неявный аргумент блока, который принимают все методы Ruby. Мы передаем содержимое переменной quux
, но сначала развернем его в блок.
Эта «развертка» на самом деле работает не только для Proc
с. &
сначала вызывает to_proc
объект, чтобы преобразовать его в процесс. Таким образом, любой объект может быть преобразован в блок.
Наиболее широко используемым примером является Symbol#to_proc
, который впервые появился где-то в конце 90-х, я считаю. Он стал популярным, когда его добавили в ActiveSupport, откуда он распространился на Facets и другие библиотеки расширений. Наконец, он был добавлен в базовую библиотеку Ruby 1.9 и перенесен в 1.8.7. Это довольно просто:
class Symbol
def to_proc
->(recv, *args) { recv.send self, *args }
end
end
%w[Hello StackOverflow].map(&:length) # => [5, 13]
Или, если вы интерпретируете классы как функции для создания объектов, вы можете сделать что-то вроде этого:
class Class
def to_proc
-> *args { new *args }
end
end
[1, 2, 3].map(&Array) # => [[nil], [nil, nil], [nil, nil, nil]]
Method
с и UnboundMethod
с Другим классом, представляющим фрагмент исполняемого кода, является класс Method
. Method
объекты являются реципиентами для методов. Вы можете создать объект Method
, вызвав Object#method
для любого объекта и передав имя метода, который вы хотите изменить:
m = 'Hello'.method(:length)
m.() #=> 5
или используя метод оператор ссылки .:
:
m = 'Hello'.:length
m.() #=> 5
Method
s отвечает на to_proc
, так что вы можете передавать их везде, где можете передать блок:
[1, 2, 3].each(&method(:puts))
# 1
# 2
# 3
An UnboundMethod
- это прокси для метода, который еще не был привязан к получателю, то есть метод, для которого self
еще не был определен. Вы не можете вызвать UnboundMethod
, но вы можете bind
сделать это для объекта (который должен быть экземпляром модуля, из которого вы получили метод), который преобразует его в Method
.
UnboundMethod
объекты создаются путем вызова одного из методов из семейства Module#instance_method
, передавая имя метода в качестве аргумента.
u = String.instance_method(:length)
u.()
# NoMethodError: undefined method `call' for #<UnboundMethod: String#length>
u.bind(42)
# TypeError: bind argument must be an instance of String
u.bind('Hello').() # => 5
Как я уже намекал выше: в Proc
и Method
s нет особенного. Любой объект , который отвечает на call
, может быть вызван, и любой объект , который отвечает на to_proc
, может быть преобразован в Proc
и, таким образом, развернут в блок и передан в метод, который ожидает блок.
Лямбда-выражение позаимствовало свою идею у Руби?
Вероятно, нет. Большинство современных языков программирования имеют некоторую форму анонимного буквального блока кода: Lisp (1958), Scheme, Smalltalk (1974), Perl, Python, ECMAScript, Ruby, Scala, Haskell, C ++, D, Objective-C, даже PHP (! ). И, конечно, вся идея восходит к «лямбда-калькуле» Алонзо Черча (1935 и даже ранее).