Основные Токенизатор Уравнение


Недавно я делал некоторые эксперименты с РПН и шунтирующих ярдов алгоритм, для того, чтобы проверить эти системы более адекватно я планировал писать токенизатор, а затем, используя эти маркеры, чтобы проверить действительность и в конечном итоге получить некий результат. Я тоже думаю, что я мог бы использовать это, чтобы работать с какой-то примитивный язык программирования, таких как чип-8 ассемблер.

Функция

Намерение состоит в том, на мой токенизатор, чтобы отделить входную строку в список следующее:

  • Отдельные Символы ('(', ')', '*', etc...)
  • Последовательности цифр ('1', '384', etc...)
  • Последовательности символов ('log', 'sin', 'x', etc...)

Обратите внимание, что из-за этого такие последовательности, как:

  • '3.14' (анализируется как '3', '.', '14')
  • '6.02E23' (анализируется как '6', '.', '02', 'E', '23')

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

Но последовательности, например, '3x' выйдет как '3', 'x' облегчая счета для умножения переменных.

Вопросы

По большей части я вполне доволен настоящим Кодексом, пару вещей, которые меня интересуют (помимо общего обзора) являются:

  • Как я могу сделать строку if l.isalpha() and buf.isdigit() or l.isdigit() and buf.isalpha(): более лаконично?
  • Насчет if buf: out += [buf]; buf = '' линии? Будет ли ничего плохого с вводом этого внутри вложенной функции в tokenize? Или out, buf = out + [buf], '' быть более подходящие для Python?
  • Эта техника делает его легче в дальнейшем для идентификации вызовов функции, такие как min, max или грех, но как бы я различать смыслы 'xy'? (x*y против переменную, на самом деле называется xyтакже этот вопрос является менее актуальным в контексте языков программирования, который будет парсить 'xy' как один токен на умножение 2)(этот вопрос, возможно, выходит за рамки ЧР, если так этот вопрос может быть удален)

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

Код

def tokenize(s):
    out = []
    buf = ''
    for l in s:
        if not l.isalnum():
            if buf:
                out += [buf]
                buf = ''
            out += [l]
        else:
            if l.isalpha() and buf.isdigit() or l.isdigit() and buf.isalpha():
                out += [buf]
                buf = ''
            buf += l
    if buf:
        out += [buf]
    return out


182
5
задан 26 января 2018 в 01:01 Источник Поделиться
Комментарии
1 ответ

1. Комментарий


  1. Нет строкой документации. Что же делать? Какое это возвращение?

  2. Результат включает в себя помещения:

    >>> tokenize('1 + 2')
    ['1', ' ', '+', ' ', '2']

    но кажется маловероятным, что эти пробелы являются значащими. Одна из полезных вещей, токенизатор может сделать, это отбросить пробел.


  3. Маркеры собираются в список и вернулся. Это негибкий, потому что вы должны ждать их, все должны быть собраны, прежде чем можно приступать к их обработке. Но анализ, как правило, используют один жетон за один раз, поэтому часто более удобным для токенизатор для создания маркеров, по одному за раз с помощью yield инструкция.

  4. Нет обнаружения ошибок:

    >>> tokenize('3E$£ω∞あ')
    ['3', 'E', '$', '£', 'ω', '∞', 'あ']

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


  5. Токенизатор не обрабатывает числа с плавающей точкой:

    >>> tokenize('3.14159')
    ['3', '.', '14159']

    или инженерной нотации:

    >>> tokenize('3e-08')
    ['3', 'e', '-', '08']

    Вы пишете в посте, что они "могут быть восстановлены позже", но было бы проще иметь токенизатор сделать это.


  6. Токенизатор не возвращает ничего, кроме самих маркеров. Как правило, одним из заданий токенизатор должен классифицировать маркеры (цифры, имена, операторы и т. д.) и в свою очередь строковых представлений чисел в сами номера.

2. Пересмотренный кодекс

В Python это часто удобно выполнить разметку, используя регулярное выражение и finditer метод. В этом случае мы можем написать:

from enum import Enum
import re

_TOKEN_RE = re.compile(r'''
\s*(?: # Optional whitespace, followed by one of:
([()^+*/-]) # Punctuation or operator
|([a-z]+) # Variable or function name
|((?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)(?:e[+-]?[0-9]+)?) # Number
|(\S)) # Anything else is an error
''', re.VERBOSE | re.IGNORECASE)

class Token(Enum):
"""Enumeration of token types."""
PUNCT = 0 # Punctuation or operator
NAME = 1 # Variable or function name
NUMBER = 2 # Number

def tokenize(s):
"""Generate tokens from the string s as pairs (type, token) where type
is from the Token enumeration and token is a float (if type is NUMBER)
or a string (otherwise).

"""
for match in _TOKEN_RE.finditer(s):
punct, name, number, error = match.groups()
if punct:
yield Token.PUNCT, punct
elif name:
yield Token.NAME, name
elif number:
yield Token.NUMBER, float(number)
else:
raise SyntaxError("Expected a token but found {!r}".format(error))

Это касается всех моих точек в §1 выше:


  1. Есть строкой документации.

  2. Пробелы отбрасываются.

  3. Токены генерируются одновременно.

  4. Ошибки выявляются и сообщил:

    >>> list(tokenize('3E$£ω∞あ'))
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "cr186024.py", line 49, in tokenize
    raise SyntaxError("Expected a token but found {!r}".format(error))
    SyntaxError: Expected a token but found '$'

  5. Токенизатор обрабатывает числа с плавающей точкой и технических обозначений:

    >>> list(tokenize('1.2 3e8 .2e-7'))
    [(<Token.NUMBER: 2>, 1.2), (<Token.NUMBER: 2>, 300000000.0), (<Token.NUMBER: 2>, 2e-08)]

  6. Токенизатор классифицирует знак и преобразует числа в floatС.

6
ответ дан 26 января 2018 в 10:01 Источник Поделиться