LINUX.ORG.RU

ruby, неправильно выполняется прок

 


0

1
$cyrillic_mac_huawei=Proc.new { #прок для quidway для предотвращиния попыток поиска по мак-адресу с кирилическми символами
  while $mac.scan(/\P{ASCII}/).size != 0 do puts "Неправильная раскладка клавиатуры. Измените на латиницу"; $mac_search_huawei.call; end
  } #конец прока
$mac_search_huawei=Proc.new { #прок поиска порта по mac'у для <<<QUIDWAY>>>; позволяет циклично повторять поиск столько раз, сколько нужно
  puts "\nВведите часть mac-адреса (Образец для Quidway: xxxx-xxxx-xxxx):" #необходимо для поиска порта
  $mac=gets.chomp
  $cyrillic_mac_huawei.call
  $host.cmd("display mac-address | include #{$mac}") {|c| $dump=c; print c} #ищем порт по части mac-адреса
  puts "Для продолжения наберите 'n', для повторного поиска - любой другой символ:"
  $mac_search_choise=gets.chomp
  until $mac_search_choise == "n" do $mac_search_huawei.call; end
  } #конец прока

Не могу понять такое поведение: запускаю скрипт, выполняю поиск по вводу, все нормально. но если набрать ввод кириллицей (будет предложение сменить раскладку и автоматически запустится заново указанный выше кусок. НО при выборе пользователя далее (для продолжения n)) - код выполняется столько раз, сколько было вводов кирилицей (2,3 и более).

код выполняется столько раз, сколько было вводов кирилицей (2,3 и более).

Код выполняется столько раз, сколько было вызовов $mac_search_huawei, и тут нет ничего удивительного.

PS. В дополнение к тому, что сказал анонимус: для определения функции/метода в Ruby используют ключевое слово def.

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

Мне кажется, ты хотел что-то типа этого:

#!/usr/bin/env ruby
# coding: utf-8

def mac_search_huawei()
  begin
    mac = prompt 'Введите часть mac-адреса (Образец для Quidway: xxxx-xxxx-xxxx):'

    if cyrillic_mac_huawei(mac)
      puts 'Неправильная раскладка клавиатуры. Измените на латиницу'
      redo
    end

    # $host.cmd("display mac-address | include #{$mac}") {|c| $dump=c; print c}
    puts mac
  end until prompt("Для продолжения наберите 'n', для повторного поиска - любой другой символ:") == 'n'
end

def prompt(str)
  print str, ' '
  $stdout.flush
  gets.chomp
end

def cyrillic_mac_huawei(mac)
  mac.scan(/\P{ASCII}/).size != 0
end
theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)
Ответ на: комментарий от theNamelessOne

Спасибо за ответы, но я привел вам только проблемный кусочек скрипта, всего там 400+ строк проков и методов, и 15-20 сам скрипт из конструкции case).

Код выполняется столько раз, сколько было вызовов $mac_search_huawei, и тут нет ничего удивительного.

Может быть вы неправильно поняли мою не в меру витиеватую задумку? Давайте я попробую еще раз: мой скрипт автоматизирует заботу на ethernet-коммутаторах, «написан» на библиотеке net/telnet. Т.к. в будущем им буду пользоваться не только я - нужно выпилить все потенциальные аварийные завершения работы скрипта. Одно из таких - ввод пользователем кириллических символов - интерпретатор то их обрабатывать умеет, а вот коммутаторы - нет. Чтобы пользователю не нужно было в случае ошибки каждый раз запускать скрипт я начал вписывать туда проки с «отловом» таких косячков и автоматическим перезапуском нужного кусочка кода. Вот ввел пользователь мак-адрес, далее выполняется команда поиска протоколом telnet в самом коммутаторе. Возвращается результат, где предложены два варианта - принять найденное, или повторить поиск, введя другие символы мак-адреса. Все прекрасно работало до тех пор, пока не начал дописывать костыть с отловом кирилицы. Есть кириллические символы - показываем сообщение и сразу автоматически предлагаем повторные ввод и поиск (прок внутри прока). Ситуация - два(три и более) раза вводим кириллицу, затем латиницу и нажимаем «продолжить» (Для продолжения наберите 'n'), здесь должен запускаться уже следующий метод, но вместо этого два(три и более) раза запускается прок $mac_search_huawei, причем ввод пользователя там уже сохранен, приходится два(три и более) раз выбирать «далее» и только потом происходит переход к следующему методу. Я пробовал переопределять в проках переменные на пустые, но это не помогло. Сталкиваюсь с такой ерундой второй раз, первый раз проблема решилась заменой проверки содержания контролируемой переменной с var != [] на var != [].size (упрощенный пример), тут не помогло.

hasculdr
() автор топика
Ответ на: комментарий от anonymous

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

hasculdr
() автор топика
Ответ на: комментарий от theNamelessOne

Спасибо, я погуглю про redo и $stdout.flush. Но все-таки хотел бы комментарий на мой пост с повторными объяснениями моего видения работы скрипта.

hasculdr
() автор топика
Ответ на: комментарий от hasculdr

Спасибо, я погуглю про redo и $stdout.flush.

Они к твоей проблеме не имеют никакого отношения, но для общего развития будет полезно.

theNamelessOne ★★★★★
()
Ответ на: комментарий от hasculdr

Может быть вы неправильно поняли мою не в меру витиеватую задумку?

Нет, я понял, но к проблеме это опять-таки отношения не имеет.

Но все-таки хотел бы комментарий на мой пост с повторными объяснениями моего видения работы скрипта.

Как я уже сказал, обрати внимание на вызовы $mac_search_huawei, а также на то, что происходит после возврата из каждого вызова. Проблема в том, что у тебя рекурсивные вызовы не хвостовые (загугли, если не знаешь, что это). Это было бы достаточно просто продемонстрировать с помощью ручки и листа бумаги, можно также увидеть, если сделать трассировку функций. Вот изменённый код с трассировкой:

# coding: utf-8

class Tracer
  def initialize()
    @top, @frame = nil, nil
  end

  def trace(name, *args)
    frame = push_frame name
    frame.enter args
    ret = yield
    frame.leave ret
    pop_frame
    ret
  end

  def pprint_trace()
    pprint @top, 0
  end

  def msg(str)
    @frame.msg str
  end

  private
  class Frame
    def initialize(name, parent=nil)
      parent.next = self if parent
      @parent = parent
      @name = name
      @events = []
    end

    attr_reader :name, :events, :parent

    def next=(value)
      event :call, value
    end

    def enter(args)
      event :enter, args
    end

    def leave(ret)
      event :leave, ret
    end

    def msg(str)
      event :msg, str
    end

    def event(type, data=nil)
      @events << [type, data]
    end
  end

  def push_frame(name)
    frame = Frame.new name, @frame
    @top ||= frame
    @frame = frame
  end

  def pop_frame()
    @frame = @frame.parent
  end

  def pprint(frame, offset)
    return unless frame
    frame.events.each do |(event, data)|
      case event
      when :enter
        pputs offset, "→ #{frame.name} args: #{data.map(&:inspect).join(', ')}"
      when :leave
        pputs offset, "← #{frame.name} returns: #{data.inspect}"
      when :call
        pprint data, offset + 2
      when :msg
        pputs offset, "? #{frame.name} msg: #{data.inspect}"
      end
    end
    nil
  end

  def pputs(offset, str)
    print ' ' * offset
    puts str
  end
end

$tracer = Tracer.new

$cyrillic_mac_huawei=Proc.new {
  $tracer.trace('$cyrillic_mac_huawei', $mac) do
    while $mac.scan(/\P{ASCII}/).size != 0 do
      puts "Неправильная раскладка клавиатуры. Измените на латиницу"
      $mac_search_huawei.call
      $tracer.msg 'after $mac_search_huawei nested call #1'
    end
  end
}

$mac_search_huawei=Proc.new {
  $tracer.trace('$mac_search_huawei') do
    puts "\nВведите часть mac-адреса (Образец для Quidway: xxxx-xxxx-xxxx):" #необходимо для поиска порта
    $mac=gets.chomp
    $cyrillic_mac_huawei.call

    $tracer.msg 'after $cyrillic_mac_huawei nested call #2'

    puts %[$host.cmd("display mac-address | include #{$mac}") {|c| $dump=c; print c}]
    puts "Для продолжения наберите 'n', для повторного поиска - любой другой символ:"

    $mac_search_choise=gets.chomp
    until $mac_search_choise == "n" do
      $mac_search_huawei.call
    end
  end
}

irb-сессия, демонстрирующая трассировку:

irb(main):020:0> $mac_search_huawei.call

Введите часть mac-адреса (Образец для Quidway: xxxx-xxxx-xxxx):
кириллица раз!
Неправильная раскладка клавиатуры. Измените на латиницу

Введите часть mac-адреса (Образец для Quidway: xxxx-xxxx-xxxx):
кириллица два!
Неправильная раскладка клавиатуры. Измените на латиницу

Введите часть mac-адреса (Образец для Quidway: xxxx-xxxx-xxxx):
a line in ASCII finally!
$host.cmd("display mac-address | include a line in ASCII finally!") {|c| $dump=c; print c}
Для продолжения наберите 'n', для повторного поиска - любой другой символ:
n
$host.cmd("display mac-address | include a line in ASCII finally!") {|c| $dump=c; print c}
Для продолжения наберите 'n', для повторного поиска - любой другой символ:
n
$host.cmd("display mac-address | include a line in ASCII finally!") {|c| $dump=c; print c}
Для продолжения наберите 'n', для повторного поиска - любой другой символ:
n
=> nil
irb(main):022:0> $tracer.pprint_trace
→ $mac_search_huawei args: 
  → $cyrillic_mac_huawei args: "кириллица раз!"
    → $mac_search_huawei args: 
      → $cyrillic_mac_huawei args: "кириллица два!"
        → $mac_search_huawei args: 
          → $cyrillic_mac_huawei args: "a line in ASCII finally!"
          ← $cyrillic_mac_huawei returns: nil
        ? $mac_search_huawei msg: "after $cyrillic_mac_huawei nested call #2"
        ← $mac_search_huawei returns: nil
      ? $cyrillic_mac_huawei msg: "after $mac_search_huawei nested call #1"
      ← $cyrillic_mac_huawei returns: nil
    ? $mac_search_huawei msg: "after $cyrillic_mac_huawei nested call #2"
    ← $mac_search_huawei returns: nil
  ? $cyrillic_mac_huawei msg: "after $mac_search_huawei nested call #1"
  ← $cyrillic_mac_huawei returns: nil
? $mac_search_huawei msg: "after $cyrillic_mac_huawei nested call #2"
← $mac_search_huawei returns: nil
=> nil
irb(main):026:0> 

→ вход в функцию (вызов функции) + её аргументы, ← выход из функции (возврат из функции) + её возвращаемое значение, ? произвольное сообщение, вложенность функций показана индентацией.

Избавиться от твоей ошибки можно любым из следующих способов:

  • сделать рекурсивные вызовы на хвостовыми;
  • переписать рекурсию на циклы (можешь адаптировать мой код, он должен работать).
theNamelessOne ★★★★★
()
Ответ на: комментарий от hasculdr

Знаю, что так плохо, но ничего конфиденциального в переменных не хранится.

Тут дело не в конфиденциальности, ты просто сам себе создаёшь проблемы, делая говнокод. Без особых на то причин ты завязываешь код на глобальные переменные вместо использования параметров и возвращаемого значения функций. Это ухудшает читаемость, мешает проследить control flow твоего кода, усложняет отладку. В приведённом тобой коде только $host более-менее подходит для использования в качестве глобальной переменной, хотя и тут можно поспорить.

Далее, зачем-то используешь Proc вместо обычных функций, что опять-таки не прибавляет читаемости твоему коду. Экономишь на переводах строки — читаемость… ну ты понял

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

Спасибо. Руби я «учил» на http://www.codecademy.com/learn/ruby, некоторые вещи мог перевести с английского некорректно. Когда гуглил про циклы (до этого момента с кириллицей), прочитал что циклы типа бейсиковского goto в ruby не предусмотрены и нужно использовать проки (т.е. в моем понимании прок в руби это и есть цикл).
Что ты называешь «функцией»?
По поводу читаемости - у меня редактор geany, там только глобальные переменные цветом и выделяются. Использую его из-за встроенного интерпретатора. Буду благодарен за наводку на другой редактор, более заточенный под руби + работающий в линуксе.

hasculdr
() автор топика
Ответ на: комментарий от hasculdr

Руби я «учил» на http://www.codecademy.com/learn/ruby, некоторые вещи мог перевести с английского некорректно.

Я читал Flanagan, Matsumoto «The Ruby Programming Language» и Brown «Ruby Best Practices», рекомендую полистать хотя бы первую книгу.

прочитал что циклы типа бейсиковского goto в ruby не предусмотрены

И не нужны они. Использование goto для организации цикла в языках уровнем выше ассемблера — это плохо, очень плохо.

и нужно использовать проки

Неверно. Proc не является аналогом цикла на goto. Это скорее анонимная функция (см. ниже).

Что ты называешь «функцией»?

Функция, метод, процедура. Именнованный фрагмент кода, который можно вызвать, передав ему определённые параметры и получив результат выполнения.

Пример:

# Функция `add` принимает два параметра `a` и `b` и возвращает их сумму:
def add(a, b)
  a + b # возвращается результат последнего выражения
end

# вызов функции `add` с параметрами 2 и 3
result = add(2, 3)

# печать результата (`puts` тоже функция)
puts result

Какой у тебя бэкграунд, если ты не знаешь, что такое функция?

По поводу читаемости - у меня редактор geany, там только глобальные переменные цветом и выделяются.

Дело не в выделении цветом (все нормальные редакторы будут выделять глобальные переменные другим цветом).

theNamelessOne ★★★★★
()
Ответ на: комментарий от hasculdr

на что только не пойдут пейсатели скриптов, чтоб пописать скрипты. Но вот только заглянуть по SNMP в вывод snmpwalk от ветки BRIDGE-MIB::dot1dTpFdbAddress - не догадаются.

Видимо обрабатывать всякие излишества китайской замудренной мысли каждый раз после смены прошивки - это героический путь ${ramdom} комсомола.

LynxChaus
()
Ответ на: комментарий от theNamelessOne

Какой у тебя бэкграунд, если ты не знаешь, что такое функция?

Я знаю что такое метод, но других его названий не встречал. Можно простейший пример метода-цикла, но при этом не прока?
Я не знаю что такое бэкграунд).

hasculdr
() автор топика
Ответ на: комментарий от hasculdr

Можно простейший пример метода-цикла, но при этом не прока?

Нет такого понятия, как «метод-цикл». Это разные вещи, которые, тем не менее, можно комбинировать.

# цикл, который выводит "hello world" три раза:
iter = 0

while iter < 3
  puts "hello world"
  iter += 1
end
# метод (функция) hello, который принимает параметр name и выводит "hello #{name} (from method)":
def hello(name)
  puts "hello #{name} (from method)"
end

hello "world"                   # вызов метода
# метод (функция) hello_times, которая принимает name и cnt
# и выводит "hello {name} (from loop in method)" cnt раз
# (цикл внутри метода)
def hello_times(name, cnt)
  iter = 0
  while iter < cnt
    puts "hello #{name} (from loop in method)"
    iter += 1
  end
  # cnt.times { puts "hello #{name} (from loop in method)" }
end

hello_times "world", 2          # вызов метода

Запусти код из этого сообщения, он делает то, что тебе нужно (разве что не запускает $host.cmd(…)), причём делает это правильно. Там есть и методы, и циклы.

Я не знаю что такое бэкграунд).

Какие языки ты знаешь? Просто из твоих сообщений сложилось такое впечатление, что ты не знаешь самых основ Ruby и программирования в общем.

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

Не знаю никаких языков, и основ программирования тоже) Только в руби 3 костыля написать смог, этот вот дорабатывать приходится постоянно.

hasculdr
() автор топика
Ответ на: комментарий от hasculdr

Не знаю никаких языков, и основ программирования тоже) Только в руби 3 костыля написать смог, этот вот дорабатывать приходится постоянно.

Ну так почитай «The Ruby Programming Language», полезно будет. А ресурсы типа codecademy, ИМХО, для обучения не подходят, так, поиграться от нечего делать.

theNamelessOne ★★★★★
()

Раби во всей красе. Один листинг красивей другого просто.

entefeed ☆☆☆
()
Ответ на: комментарий от theNamelessOne

«Переделал» для себя вот так:

def mac_search_huawei
  begin
    puts "\nВведите часть mac-адреса (Образец для Quidway: xxxx-xxxx-xxxx):" #необходимо для поиска порта
    mac=gets.chomp
    if mac.scan(/\P{ASCII}/).size != 0 then puts "Неправильная раскладка клавиатуры. Измените на латиницу"
      redo
    end
    #host.cmd("display mac-address | include #{$mac}") {|c| $dump=c; print c} #ищем порт по части mac-адреса
    puts "Для продолжения нажмитете 'Enter', для повторного поиска наберите любой другой символ:"
    mac_search_choise=gets.chomp
    end until mac_search_choise == ""
end
Немного не понял строку
end until...
- в ней end относится и к begin и к until одновременно?

hasculdr
() автор топика
Ответ на: комментарий от hasculdr

- в ней end относится и к begin и к until одновременно?

Да.

PS. Больше на вопросы, ответы на которые можно элементарно найти в книжке/гугле, я отвечать не буду.

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

Спасибо. PS. Элементарно находятся ответы только на правильные вопросы, а я их «спросить» не могу. Я честно пользуюсь поиском перед тем как спрашивать.

hasculdr
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.