Преобразование строки в число на Prolog

      Комментарии к записи Преобразование строки в число на Prolog отключены

В этой теме 2 ответа, 2 участника, последнее обновление  Васильев Владимир Сергеевич 1 год, 8 мес. назад.

  • Автор
    Сообщения
  • #2485

    questioner
    Участник

    Здравствуйте. Иногда нужно получить от пользователя число, но необходимо проверить правильно ли он его ввел. Кроме того, такая задача может возникать при считывании данных с файла.
    Сейчас мне нужно считать число, находящееся в начале «строки» и получить ее остаток. Число может быть как целым, так и дробным, т.е. допустимы варианты: «123», «.123», «54.67». Пусть число будет всегда положительным (при необходимости знак я сам добавлю).

  • #2487

    Здравствуйте. Есть много вариантов решения задачи — на SWI Prolog есть почти готовые предикаты, с помощью которых задача решается легко и просто. В Visual Prolog функции ввода являются типизированными (например readint считывает целое число) и, если считать число не получается, — они завершаются неудачей без побочных эффектов (не изменяя состояния буфера ввода).

    Однако, такая задача реально может возникать если мы выполняем разбор данных в каком-то хитром формате. Я не уверен, что readint сработает правильно на строке типа «123.456-123».

  • #2488

    Алгоритм разбора числа должен отделять от строки символы и проверять являются ли они цифрой, поэтому пригодится функция для проверки цифр:

    symbol_to_digit(DigitSymbol, Digit):-
      DigitSymbol >= 0'0, DigitSymbol =< 0'9,
      Digit is DigitSymbol - 0'0.

    Тут 0'9 — это код цифры 9, такой вариант работает в SWI Prolog и многих других диалектах. Если в вашем диалекте такой синтаксис не поддерживается — можно использовать ASCII коды символов — 48 и 58 соответственно.

    Наша функция должна принимать строку и возвращать первое число из нее и остаток строки.

    get_positive(String, Number, TailString):-
      symbol_to_digit(HeadDigit, _Digit), !,
      get_positive(integral, 0, String, Number, TailString).

    Мы вызываем вспомогательную функцию, при этом первый параметр integral означает, что сейчас вызываются правила считывания целой части, при этом начальное значение результата равно нулю.

    Вспомогательная функция должна:

      • переходить на обработку дробной части если встречен символ точка, результат при этом формируется как сумма целой и дробной частей.
      • считывать первую цифру числа и добавлять ее к накопленному результату, умноженному на 10;
      • возвращать накопленный результат, если первый символ не является цифрой:

      get_positive(integral, IntegralPart, [0'.|Tail], Number, TailString):-
        !, get_positive(fraction, 0, 10, Tail, FractionPart, TailString),
        Number is IntegralPart + FractionPart.
      get_positive(integral, IntegralPart, [HeadDigit|Tail], Number, TailString):-
        symbol_to_digit(HeadDigit, Digit), !,
        TailIntegralPart is IntegralPart*10 + Digit,
        get_positive(integral, TailIntegralPart, Tail, Number, TailString).
      get_positive(integral, Number, String, Number, String):-!.

      При обработке дробной части очередная цифра числа должна делиться на множитель, изначально равный 10, но увеличивающий в 10 раз при каждой итерации. Первый символ, отличный от цифры будет означать, что число кончилось и нужно вернуть накопленный результат:

      get_positive(fraction, FractionPart, Factor, [HeadDigit|Tail], Number, TailString):-
        symbol_to_digit(HeadDigit, Digit), !,
        TailFractionPart is FractionPart + Digit/Factor,
        TailFactor is Factor*10,
        get_positive(fraction, TailFractionPart, TailFactor, Tail, Number, TailString).
      get_positive(fraction, Number, _TailFactor, String, Number, String).

    Для ответа в этой теме необходимо авторизоваться.