Выбрать главу

require ’Stack’ class Compf < Stack def compile(str)

     "(#{str})".each_byte { |c| processSymbol(c.chr) }

end

     private def symType(c) case c

when ’(’

     SYM_LEFT when ’)’

     SYM_RIGHT when ’+’, ’-’, ’*’, ’/’

     SYM_OPER

     else

           symOther(c)

     end

end

def processSymbol(c)

case symType(c)

when SYM_LEFT

     push(c)

when SYM_RIGHT

     processSuspendedSymbol(c)

     pop

when SYM_OPER

     processSuspendedSymbol(c)

     push(c)

when SYM_OTHER

     nextOther(c)

     end

end

def processSuspendedSymbol(c)

while precedes(top, c)

     nextOper(pop)

     end

end

Работа начинается с вызова метода compile, в котором все символы строки str последовательно передаются методу processSymbol.

def priority(c)

     (c == ’+’ or c == ’-’) ? 1 : 2 end

def precedes(a, b)

     return false if symType(a) == SYM_LEFT

return true

if symType(b) == SYM_RIGHT

     priority(a) >= priority(b) end

     protected

     SYM_LEFT = 0; SYM_RIGHT = 1; SYM_OPER = 2; SYM_OTHER = 3

def symOther(c)

# Сравнение символа с образцом (регулярным выражением).

raise "Недопустимый символ #{c}" if c !~ /[a-z]/

     SYM_OTHER

end

def nextOper(c)

     print "#{c} "

end

def nextOther(c)

     nextOper(c)

     end

end

Квалификатор доступа protected и метод nextOther нужны для создания на базе класса Compf нового класса Calc, реализующего калькулятор формул[13].

Класс Calc (калькулятор числовых формул) выведен из класса Compf, переопределяет некоторые методы последнего, и имеет дополнительный стек для размещения в нём чисел. Калькулятор работает только с цифрами (числами от 0 до 9).

Несколько комментариев к методу nextOper(c) класса Calc. Множественное присваивание в первой строке метода корректно, т.к. в языке Ruby при выполнении множественного (параллельного) присваивания сначала последовательно вычисляются все выражения в правой части оператора присваивания.

require ’Compf’

class Calc < Compf def initialize

# Вызов метода initialize базового класса Compf. super

# Создание стека результатов операций.

@s = Stack.new

end

def compile(str) super

return @s.top end

protected

def symOther(c)

raise "Недопустимый символ #{c}" if c !~ /[0-9]/ SYM_OTHER end

def nextOper(c)

second, first = @s.pop, @s.pop @s.push(first.method(c).call(second)) end

def nextOther(c)

@s.push(c.to_i)

end

end

Конструкция first.method(c).call(second) во второй строке метода может быть объяснена таким примером: выражение 3.metod(’-’).call(2), эквивалентно выражению 3. – (2) или просто 3-2.

Задача 1. Добавьте операции sin, cos и унарный минус.

Задача 2. Добавьте правоассоциативную операцию ~ возведения в степень.

Задача 3. Добавьте квадратные и фигурные скобки.

Задача 4. Измените программу так, чтобы допускались в качестве имен переменных произвольные идентификаторы языка Ruby.

Задача 5. Добавьте левоассоциативную операцию % с приоритетом, равным приоритету операции /.

Задача 6. Добавьте возможность записи формулы с пробелами и комментариями двух типов (/* */ и //).

Задача 7. Измените программу так, чтобы ввод, содержащий в качестве аргументов только восьмеричные числа (начинающиеся с нуля, например 056), компилировался в программу, содержащую десятичные числа.

Задача 8. Измените программу так, чтобы ввод, содержащий в качестве аргументов только шестнадцатеричные числа (начинающиеся с 0x, например 0x56), компилировался в программу, содержащую восьмеричные числа.

Задача 9. Измените программу так, чтобы ввод, содержащий в качестве аргументов только римские числа, не превосходящие 5000, компилировался в программу, содержащую десятичные числа.

Задача 10. Измените программу так, чтобы для коммутативной операции аргументы выдавались в алфавитном порядке.

Задача 11. Добавьте фигурные скобки, означающие возведение в квадрат. Используйте операцию DUP стекового калькулятора.

Задача 12. Считая, что a = 0, оптимизируйте формулу (уберите лишние сложения).

Задача 13. Считая, что b = 1, оптимизируйте формулу (уберите лишние умножения).

Задача 14. Добавьте возможность ввода формулы на нескольких строках.

вернуться

[13]

Хотя в языке Ruby в данном случае можно убрать "protected", тем самым размещая все нижеописываемые константы и методы в зоне действия квалификатора private, в языках Java и C++ здесь нужен именно квалификатор protected.