Думайте об этом как об удивительном языке программирования внутри языка

Недавно я обнаружил кое-что очень интересное, работая над своей следующей книгой - сборником коротких программ на Python для портативного калькулятора Casio fx-9750GIII. Я поделюсь полученной программой здесь (она отлично работает в любой версии Python) и объясню, как замечательная функция eval () спасла меня.

Язык внутри языка

Программа, над которой я работал, позволяет студентам находить корни любой функции от x или точки, в которых функция пересекает ось x, когда она отображается на графике. Вместо того, чтобы заставлять пользователя редактировать программу для определения некоторой функции x, я задавался вопросом, можно ли найти способ, позволяющий пользователю вводить функцию динамически во время выполнения, точно так же, как при вводе значения любой другой переменной с использованием ввода Python ( ) функция. Функция eval () позволяет нам делать именно это и многое другое!

Как работает eval ()

Строка, переданная в eval (), обрабатывается Python как выражение Python. Например, внимательно посмотрите на этот короткий сценарий:

from math import *
s = input("? ")
print(eval(s))

Здесь пользователь может ввести во время выполнения широкий спектр вычислений, например, этот для определения площади круга с радиусом 17:

? pi * 17 ** 2
907.9202768874502

Этот простой пример намекает на то, насколько легко с помощью eval () создать крутой и мощный калькулятор Python всего в несколько строк кода или, возможно, создать совершенно новый язык сценариев с использованием Python в качестве оболочки.

Casio MicroPython

Версия MicroPython, которая работает в нескольких калькуляторах Casio, включая экономичный FX-9750GIII, на который нацелена моя новая книга, очень хорошо справляется с математическими задачами. Одно ограничение заключается в том, что есть только три библиотеки, которые можно импортировать - math, random и casioplot, но большинство встроенных функций и структур данных есть, включая функцию eval (). Версия eval () Casio для MicroPython имеет небольшое ограничение, которое отличает ее от некоторых других версий Python. Переменные, названные в строке, переданной в eval (), не обрабатываются должным образом. Я нашел простой обходной путь, который я покажу и объясню с помощью этой короткой программы, которая находит корни введенной во время выполнения функции x:

from math import *
def f(g,x):
  g=g.replace('x','('+str(x)+')')
  return eval(g)
def zero(g,x1,x2):
  lst=0
  y1=f(g,x1)
  y2=f(g,x2)
  while 1:
    if y1*y2>0:
      return float("nan") 
    x=(x1+x2)/2
    y=f(g,x)
    if y1*y>0:
      x1=x
      y1=f(g,x1)
    else:
      x2=x
      y2=f(g,x2)
    if x1-x2!=lst:
      lst=x1-x2
    else:
      return x
g=input('\nf(x)? ').lower()
while 1:
  try:
    x1=float(input('\nx1? '))    
    x2=float(input('x2? '))
    x0=zero(g,x1,x2)
    print('root: ',x0)
    print('f(x)=',g)
  except:
    break

Первые несколько строк выше представляют основное действие. В функцию f () передается введенная пользователем строка с именем g, которая содержит функцию x в стандартном синтаксисе Python и значение x, которое должно быть оценено с использованием этой строки. Он возвращает значение y, вычисленное с использованием данной функции x.

Замена «x» в строке

Строка, такая как «2 * x», не может вернуть удвоенное значение x, потому что функция eval (), очевидно, не знает, как сделать правильную подстановку значения x в строку. Итак, вторая строка приведенного выше кода позволяет обойти эту проблему, используя функцию replace () строки для вставки строкового представления значения x в строку. Чтобы избежать случайных проблем с отрицательными значениями x, добавляются круглые скобки, чтобы обернуть и изолировать значение.

Корнеплоды

Функция zero () принимает функцию x в строковой форме, введенной пользователем, плюс два значения x, и выполняет двоичный поиск, чтобы найти значение x, где функция равна нулю. Пока два начальных значения x оцениваются как величины с противоположными знаками, непрерывная функция x всегда будет иметь корень где-то между этими двумя значениями.

Рабочий пример

Рассмотрим эту функцию от x:

Быстрый график этой функции показывает, что есть три корня в районе -5, -1 и +1. Этот график, взятый из моего калькулятора HP Prime, показывает один из корней при x = 0,828427124746.

Вот как программа Python, представленная выше, находит эти корни, используя IDLE на настольном компьютере Windows (она работает так же в моих калькуляторах Casio или Numworks):

Первая строка показывает, как функция вводится во время выполнения в стандартном синтаксисе Python. Затем программа зацикливается, чтобы запросить значения x для нахождения каждого корня в скобках. Обратите внимание, что значения x можно вводить в любом порядке, где второе значение может быть больше или меньше первого.

Компактный код

Эта программа была создана для моего калькулятора Casio. Вы могли заметить, что уровень отступа установлен на 2 вместо 4, и между различными математическими операциями нет пробелов. Этот стиль компактного кода экономит байты в ограниченном пространстве памяти калькулятора, о чем нам не нужно думать в мире настольных компьютеров и ноутбуков, оставаясь при этом легко читаемым - отличительной чертой всего кода Python.

Еще идеи для eval ()

Функция eval () в Python открывает дверь к очень мощным возможностям программирования. Очевидным является создание интерактивной программы-калькулятора, которая действует аналогично среде оболочки Python, но, возможно, оформлена в творческом окне tKinter или Pygame. Можно было бы создать версию языка, подобного BASIC, где обработка строк могла бы использоваться для синтаксического анализа команд в эквивалентном синтаксисе Python, а функция eval () могла бы выполнять тяжелую работу в основных вычислениях. И, конечно, было бы интересно создать калькулятор RPN, аналогичный мощным ранним калькуляторам HP, но, возможно, обновленный новыми командами для обработки строк и других типов переменных.

Осторожность!

Имейте в виду, что при некоторых обстоятельствах функция eval () может вызвать проблемы. Например, если вы импортируете модуль ОС и запускаете eval () для определенных строковых команд, можно удалить целые каталоги или вызвать другие проблемы. Будь осторожен. Но получайте удовольствие. Вы можете сделать и то, и другое!

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

Спасибо за чтение!

Страсть и миссия Джона - делиться кодом Python, чтобы помочь развенчать загадки жизненных проблем и повеселиться. Джон является автором Python для NumWorks, Python для OpenSCAD и многих других книг.