Код
def evaluate(arr)
pairs = arr.drop(1).each_slice(2)
pairs.reduce(arr.first) { |rv, (op, bool)| op == '&&' ? rv && bool : rv || bool }
end
Примеры
evaluate [true, '&&', false, '||', true] #=> true
evaluate [false, '||', true, '&&', false] #=> false
Пояснение
Предположим
arr = [true, '&&', false, '||', true]
Затем
a = arr.drop(1) #=> ["&&", false, "||", true]
pairs = a.each_slice(2) #=> #<Enumerator: ["&&", false, "||", true]:each_slice(2)>
Мы можем видеть, какие элементы перечислитель pairs
отправит в блок, преобразовав его в массив.
pairs.to_a #=> [["&&", false], ["||", true]]
Продолжение(rv
является коротким для возвращаемого значения ), мы генерируем первый элемент pairs
, передаем его в блок и присваиваем значения переменным блока op
и bool
.
rv = arr.first #=> true
op, bool = pairs.next #=> ["&&", false]
op #=> "&&"
bool #=> false
Теперь выполним расчет блока.Поскольку
op == '&&' #=> true
блок вычисляет и возвращает следующее, которое является новым значением памятки , rv
:
rv && bool #=> true && false => false
Теперь мы генерируемвторой и последний элемент pairs
, передайте его в блок, присвойте значения переменным блока (вспомните, что rv
теперь равно false
) и выполните вычисление блока.
op, bool = pairs.next #=> ["||", true]
op #=> "||"
bool #=> true
op == '&&' #=> false
rv || bool #=> false || true #=> true
Таким образом, методвозвращает true
.
Использование Kernel # eval вместо
Если мы доверяем значениям в массиве, мы могли бы просто написать
eval [true, '&&', false, '||', true].join #=> true
eval [false, '||', true, '&&', false].join #=> false
Использование eval
позволило бы нам добавить "("
, ")" and
"!" `В массив, что значительно увеличило диапазон логических выражений, которые можно было бы оценить (легко).