Иконка (язык программирования) - Icon (programming language)

Значок
Значок logo.png
Парадигма мультипарадигма : структурированная , ориентированная на текст
Разработано Ральф Грисволд
Впервые появился 1977 ; 44 года назад ( 1977 )
Стабильный выпуск
9.5.20 / 13 августа 2020 г . ; 13 месяцев назад ( 2020-08-13 )
Печатная дисциплина динамичный
Веб-сайт www .cs .arizona .edu / icon
Основные реализации
Значок, Jcon
Диалекты
Юникон
Под влиянием
SNOBOL , SL5, Алгол
Под влиянием
Юникон, Python , Goaldi

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

Иконка была разработана Ральфом Грисволдом после ухода из Bell Labs, где он был одним из основных разработчиков языка SNOBOL . SNOBOL был языком обработки строк с синтаксисом, который по стандартам начала 1970-х считался устаревшим. После переезда в Университет Аризоны он продолжил разработку основных концепций SNOBOL в SL5, но счел результат неудачным. Это привело к значительному обновлению Icon, который сочетает в себе короткий, но концептуально насыщенный код языков, подобных SNOBOL, с более знакомым синтаксисом языков, вдохновленных ALGOL, таких как C или Pascal .

Как и языки, которые вдохновили его, основная область использования Icon - это управление строками и текстовыми шаблонами. Строковые операции часто терпят неудачу, например, найти «the» в «world». На большинстве языков это требует тестирования и ветвления, чтобы избежать использования недействительного результата. В Icon большинство таких тестов просто не требуются, что сокращает объем кода, написанного программистом. Обработка сложных шаблонов может быть выполнена в нескольких строках краткого кода, аналогичного более специализированным языкам, таким как Perl, но с сохранением более функционально-ориентированного синтаксиса, знакомого пользователям других языков, подобных АЛГОЛу.

Icon не является объектно-ориентированным , но в 1996 году было разработано объектно-ориентированное расширение под названием Idol, которое в конечном итоге стало Unicon . Он также вдохновил другие языки, особенно на него повлияли его простые генераторы; Генераторы иконок послужили основным источником вдохновения для языка программирования Python .

История

СНОБОЛ

Первоначальная разработка SNOBOL , задним числом известная как SNOBOL1, была начата осенью 1962 года в отделе исследований программирования Bell Labs . Эти усилия были реакцией на разочарование при попытке использовать язык SCL для манипулирования полиномиальными формулами, символической интеграции и изучения цепей Маркова . SCL, написанный главой отдела Честером Ли, был медленным и имел низкоуровневый синтаксис, что приводило к объемам кода даже для простых проектов. После краткого изучения языка COMIT, Иван Полонский, Ральф Грисволд и Дэвид Фарбер, все члены отдела из шести человек, решили написать свой собственный язык для решения этих проблем.

Первые версии работали на IBM 7090 в начале 1963 года, а к лету были построены и использовались в Bell. Это почти сразу привело к появлению SNOBOL2, в котором был добавлен ряд встроенных функций и возможность связываться с кодом внешнего языка ассемблера . Он был выпущен в апреле 1964 года и в основном использовался в Bell, но также нашел применение в Project MAC . Введение системных функций в основном служило указанием на потребность в пользовательских функциях, что было основной особенностью SNOBOL3, выпущенного в июле 1964 года.

Введение SNOBOL3 совпало с основными изменениями в вычислительном отделе Bell Labs, включая добавление нового мэйнфрейма GE 645, что потребовало бы переписывания SNOBOL. Вместо этого команда предложила написать новую версию, которая будет работать на виртуальной машине , под названием SIL для промежуточного языка SNOBOL, что позволит легко портировать ее на любую достаточно мощную платформу. Это предложение было принято как SNOBOL4 в сентябре 1965 года. К этому времени в августе 1966 года появились планы по значительно улучшенной версии языка. Дальнейшая работа над языком продолжалась до конца 1960-х годов, в частности добавление типа ассоциативного массива в более позднюю версию. , который они назвали таблицей.

SL5 ведет к Icon

Грисволд покинул Bell Labs, чтобы стать профессором Университета Аризоны в августе 1971 года. В то время он представил SNOBOL4 как инструмент исследования.

Синтаксис SNOBOL, первоначально разработанный в начале 1960-х годов, имеет следы других ранних языков программирования, таких как FORTRAN и COBOL . В частности, язык зависит от столбца, поскольку многие из этих языков были введены на перфокартах, где расположение столбцов является естественным. Кроме того, управляющие структуры почти полностью основывались на ветвлении кода, а не на использовании блоков , которые становились обязательной функцией после появления АЛГОЛА 60 . К тому времени, как он переехал в Аризону, синтаксис SNOBOL4 безнадежно устарел.

Грисволд начал работу по реализации лежащей в основе концепции успеха / неудачи SNOBOL с традиционных структур управления потоками, таких как если / тогда. Это стало SL5, сокращенно от «SNOBOL Language 5», но результат был неудовлетворительным. В 1977 году он вернулся к языку, чтобы рассмотреть новую версию. Он отказался от очень мощной системы функций, представленной в SL5, с более простой концепцией приостановки / возобновления и разработал новую концепцию естественного преемника SNOBOL4 со следующими принципами;

  • Философско-смысловая основа СНОБОЛ4
  • Синтаксическая основа SL5
  • Возможности SL5, за исключением механизма обобщенных процедур

Новый язык был первоначально известен как SNOBOL5, но, поскольку он значительно отличался от SNOBOL во всем, кроме лежащей в основе концепции, в конечном итоге потребовалось новое имя. После рассмотрения «s» как своего рода дань уважения «C», но в конечном итоге от него отказались из-за проблем с набором документов с использованием этого имени. Были предложены и отвергнуты серии новых имен; Ирвинг, бард и "TL" для "The Language". Это было в то время, когда Xerox PARC начала публиковать материалы о своей работе над графическими пользовательскими интерфейсами, и термин «значок» начал входить в компьютерный лексикон. Было принято решение сначала изменить имя на «icon», прежде чем окончательно выбрать «Icon».

Язык

Базовый синтаксис

Язык Icon является производным от ALGOL -класса структурированных языков программирования и, таким образом, имеет синтаксис, аналогичный C или Pascal . Icon больше всего похож на Pascal, используя :=синтаксис для назначений, procedureключевое слово и аналогичный синтаксис. С другой стороны, Icon использует фигурные скобки в стиле C для структурирования групп выполнения, и программы запускаются с запуска вызываемой процедуры main.

Во многих отношениях Icon также имеет общие функции с большинством языков сценариев (а также с SNOBOL и SL5, из которых они были взяты): переменные не нужно объявлять, типы приводятся автоматически, числа можно преобразовывать в строки и обратно автоматически. Другой особенностью, общей для многих языков сценариев, но не для всех, является отсутствие символа окончания строки; в Icon строки, которые не заканчиваются точкой с запятой, заканчиваются подразумеваемой точкой с запятой, если это имеет смысл.

Процедуры - это основные строительные блоки программ Icon. Хотя они используют именование Pascal, они работают больше как функции C и могут возвращать значения; functionв Icon нет ключевого слова.

 procedure doSomething(aString)
   write(aString)
 end

Целенаправленное исполнение

Одна из ключевых концепций SNOBOL заключалась в том, что его функции возвращали «успех» или «неудачу» как примитивы языка, а не использовали магические числа или другие методы. Например, функция, которая возвращает позицию подстроки в другой строке, является обычной процедурой, встречающейся в большинстве языковых систем времени выполнения ; например, в JavaScript можно найти позицию слова «World» в «Hello, World!», что будет завершено с помощью , что вернет 7. Если вместо этого запросить код, код «потерпит неудачу», поскольку поисковый запрос не появляется в строке. В JavaScript, как и в большинстве языков, это будет обозначено возвращением магического числа, в данном случае -1. position = "Hello, World".indexOf("World")position = "Hello, World".indexOf("Goodbye")

В СНОБОЛ такой сбой возвращает специальное значение fail. Синтаксис SNOBOL напрямую влияет на успешность или неудачу операции, переходя к помеченным участкам кода без написания отдельного теста. Например, следующий код печатает «Hello, world!» пять раз:

* SNOBOL program to print Hello World
      I = 1
LOOP  OUTPUT = "Hello, world!"
      I = I + 1
      LE(I, 5) : S(LOOP)
END

Для выполнения цикла LEдля индексной переменной I вызывается оператор «меньше или равно» , и если он Sзавершается, то есть I меньше 5, выполняется переход к названной метке LOOPи продолжается.

Icon сохранил базовую концепцию управления потоком, основанную на успехе или неудаче, но развил язык дальше. Одним из изменений была замена маркированного GOTOветвления блочно-ориентированными структурами в соответствии со стилем структурированного программирования, который охватил компьютерную индустрию в конце 1960-х годов. Вторая заключалась в том, чтобы разрешить передачу «отказа» по цепочке вызовов, чтобы целые блоки были успешными или неудачными в целом. Это ключевая концепция языка иконок. В то время как в традиционных языках нужно было бы включать код для проверки успеха или неудачи на основе логической логики, а затем переходить в зависимости от результата, такие тесты и переходы являются неотъемлемой частью кода Icon и не должны быть написаны явно.

Например, рассмотрим этот фрагмент кода, написанный на языке программирования Java . Он вызывает функцию read()для чтения символа из (ранее открытого) файла, присваивает результат переменной a, а затем writeпередает значение aдругому файлу. В результате один файл копируется в другой. readв конечном счете закончится символы для чтения из файла, потенциально на самый первый вызов, который оставил бы aв неопределенном состоянии и потенциально вызвать , writeчтобы вызвать исключение пустого указателя . Чтобы избежать этого, в этой ситуации readвозвращает специальное значение EOF(конец файла), что требует явной проверки, чтобы избежать writeэтого:

 while ((a = read()) != EOF) {
   write(a);
 }

Напротив, в Icon read()функция возвращает строку текста или &fail. &failне является просто аналогом EOF, как это явно понимается языком как означающее «остановить обработку» или «выполнить случай отказа» в зависимости от контекста. Эквивалентный код в Icon:

 while a := read() then write(a)

Это означает: «пока чтение не возвращает ошибку, вызовите запись, в противном случае остановите». Нет необходимости указывать тест на магическое число, как в примере Java, это неявно, и результирующий код упрощен. Поскольку успех и неудача передаются по цепочке вызовов, можно встраивать функции в другие, и они останавливаются, когда вложенная функция дает сбой. Например, приведенный выше код можно сократить до:

 while write(read())

В этой версии, если readвыходит из строя, writeвыходит из строя и whileостанавливается. Конструкции ветвления и цикла Icon основаны на успехе или неудаче кода внутри них, а не на произвольном логическом тесте, предоставленном программистом. ifвыполняет thenблок, если его «тест» возвращает значение, и elseблок или переходит на следующую строку, если возвращается &fail. Точно так же whileпродолжает вызывать свой блок, пока не получит ошибку. Икона называет это понятие целенаправленным исполнением .

Важно противопоставить концепции успеха и неудачи концепции исключения ; исключения - это необычные ситуации, а не ожидаемые результаты. Неудачи в Icon - ожидаемые результаты; достижение конца файла - это ожидаемая ситуация, а не исключение. Icon не имеет обработки исключений в традиционном смысле, хотя отказ часто используется в ситуациях, подобных исключениям. Например, если читаемый файл не существует, происходит readсбой без указания особой ситуации. На традиционном языке эти «другие условия» не имеют естественного способа обозначения; могут использоваться дополнительные магические числа, но чаще используется обработка исключений для «выброса» значения. Например, чтобы обработать отсутствующий файл в коде Java, можно увидеть:

 try {
   while ((a = read()) != EOF) {
     write(a);
   }
 } catch (Exception e) {
   // something else went wrong, use this catch to exit the loop
 }

В этом случае необходимо два сравнения: одно для EOF и другое для всех остальных ошибок. Поскольку Java не позволяет сравнивать исключения как логические элементы, как в случае с Icon, try/catchвместо этого должен использоваться длинный синтаксис. Блоки try также приводят к снижению производительности, даже если исключение не генерируется, - распределенные затраты , которых обычно избегает Icon.

Icon использует тот же целевой механизм для выполнения традиционных логических тестов, хотя и с небольшими отличиями. Простое сравнение, например , не означает, «если операции справа оцениваются как истинные», как это было бы в большинстве языков; вместо этого это означает что-то вроде «если операции справа завершились успешно . В этом случае оператор < завершается успешно, если сравнение истинно. Оператор вызывает свое предложение, если выражение выполнено успешно, или следующую или следующую строку, если оно не выполняется. результат аналогичен традиционному if / then в других языках, выполняется if меньше чем . Тонкость заключается в том, что одно и то же выражение сравнения можно разместить где угодно, например: if a < b then write("a is smaller than b")ifthenelseifthenab

 write(a < b)

Другое отличие состоит в том, что <оператор возвращает свой второй аргумент в случае успеха, что в этом примере приведет к тому, что значение bбудет записано, если оно больше a, в противном случае ничего не будет записано. Поскольку это не тест сам по себе , а оператор, возвращающий значение, их можно связать вместе, допуская такие вещи, как if a < b < cобщий тип сравнения, который на большинстве языков должен быть записан как соединение двух неравенств, например if (a < b) && (b < c).

Ключевым аспектом целенаправленного выполнения является то, что программе, возможно, придется вернуться к более раннему состоянию, если процедура не удалась, и эта задача известна как обратное отслеживание . Например, рассмотрим код, который устанавливает переменную в начальное положение, а затем выполняет операции, которые могут изменить значение - например, это обычное явление в операциях сканирования строки, когда курсор перемещается по строке при сканировании. Если процедура завершается неудачно, важно, чтобы любые последующие чтения этой переменной возвращали исходное состояние, а не состояние, в котором оно было внутренне изменено. Для решения этой задачи, Icon имеет обратимое назначение оператор, <-и на обратимый обмен , <->. Например, рассмотрим код, который пытается найти строку шаблона в более крупной строке:

 {
   (i := 10) &
   (j := (i < find(pattern, inString)))
 }

Этот код начинается с перехода iк 10, начальному местоположению поиска. Однако в случае findнеудачи блок будет отказывать в целом, что приведет к тому, что значение iбудет оставлено равным 10 как нежелательный побочный эффект . Замена i := 10на i <- 10указывает, что iв случае сбоя блока следует сбросить его до предыдущего значения. Это обеспечивает аналог атомарности в исполнении.

Генераторы

Выражения в Icon могут возвращать одно значение, например, x < 5будут оценивать и возвращать x, если значение x меньше 5, или иначе не получится. Однако Icon также включает в себя концепцию процедур, которые не возвращают сразу успех или неудачу, а вместо этого возвращают новые значения каждый раз при их вызове. Они известны как генераторы и являются ключевой частью языка иконок. Выражаясь языком Icon, оценка выражения или функции производит последовательность результатов . Результирующая последовательность содержит все возможные значения, которые могут быть сгенерированы выражением или функцией. Когда последовательность результатов исчерпана, выражение или функция не выполняется.

Icon позволяет любой процедуре возвращать одно значение или несколько значений, управляемый с помощью fail, returnи suspendключевые слова. Процедура, в которой отсутствует какое-либо из этих ключевых слов, возвращается &fail, что происходит всякий раз, когда выполнение выполняется до endпроцедуры. Например:

 procedure f(x)
   if x > 0 then {
     return 1
   }
 end

Вызов f(5)вернет 1, но вызов f(-1)вернется &fail. Это может привести к неочевидному поведению, например, write(f(-1))ничего fне выводится из-за сбоя и приостанавливает работу write.

Для преобразования процедуры в генератор используется suspendключевое слово, которое означает «вернуть это значение и при повторном вызове начать выполнение с этого момента». В этом отношении это что-то вроде комбинации staticконцептов C и return. Например:

 procedure ItoJ(i, j)
   while i <= j do {
     suspend i
     i +:= 1
   }
   fail
 end

создает генератор, который возвращает ряд чисел, начиная с iи заканчивая a j, а затем возвращается &failпосле этого. suspend iостанавливает выполнение и возвращает значение iбез переустановки любого государства. Когда другой вызов той же функции выполняется, выполнение начинается с этого момента с предыдущими значениями. В этом случае это заставляет его выполнить i +:= 1, вернуться к началу блока while, а затем вернуть следующее значение и снова приостановить. Это продолжается до тех пор i <= j, пока не произойдет сбой, после чего он выходит из блока и вызывает fail. Это позволяет легко создавать итераторы .

Другой тип генератора-строителя - генератор переменного тока , который выглядит и работает как логический orоператор. Например:

 if y < (x | 5) then write("y=", y)

Кажется, что это говорит «если y меньше, чем x или 5, то ...», но на самом деле это сокращенная форма для генератора, который возвращает значения, пока они не упадут за конец списка. Значения списка «вводятся» в операции, в данном случае <. Итак, в этом примере система сначала проверяет y <x, если x действительно больше, чем y, она возвращает значение x, тест проходит, и значение y записывается в thenпредложении. Однако, если x не больше, чем y, он выходит из строя, и генератор продолжает работать, выполняя y <5. Если этот тест проходит, y записывается. Если y не меньше ни x, ни 5, генератор выходит из строя и выходит из ifстроя, выходит из строя и writeне выполняется. Таким образом, значение y будет отображаться на консоли, если оно меньше x или 5, тем самым выполняя назначение логического значения or. Функции не будут вызываться, если оценка их параметров не будет успешной, поэтому этот пример можно сократить до:

 write("y=", (x | 5) > y)

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

 every i := (1|3|4|5|10|11|23) do write(i)

Поскольку списки целых чисел обычно встречаются во многих контекстах программирования, Icon также включает toключевое слово для создания специальных генераторов целых чисел:

 every k := i to j do write(k)

который можно сократить:

 every write(1 to 10)

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

 every i := (1 | "hello" | x < 5)  do write(i)

Это записывает 1, «привет» и, возможно, 5 в зависимости от значения x.

Аналогично, оператор конъюнкции , &используется аналогично логическому andоператору:

 every x := ItoJ(0,10) & x % 2 == 0 do write(x)

Этот код вызывает ItoJи возвращает начальное значение 0, которое присваивается x. Затем он выполняет правую часть конъюнкции, и, поскольку x % 2он равен 0, он записывает значение. Затем он снова вызывает ItoJгенератор, который присваивает 1 переменной x, который не работает с правой стороны и ничего не печатает. Результатом является список всех четных целых чисел от 0 до 10.

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

 s = "All the world's a stage. And all the men and women merely players";
 i = indexOf("the", s);
 write(i);

Это просканирует строку s, найдет первое вхождение «the» и вернет этот индекс, в данном случае 4. Строка, однако, содержит два экземпляра строки «the», поэтому для возврата во втором примере альтернативный синтаксис: использовал:

 j = indexOf("the", s, i+1);
 write(j);

Это указывает ему сканировать, начиная с местоположения 5, поэтому он не будет соответствовать первому экземпляру, который мы нашли ранее. Однако может не быть второго экземпляра «the» - может не быть и первого - поэтому возвращаемое значение indexOfдолжно быть проверено по магическому числу -1, которое используется для обозначения отсутствия совпадений. Полная процедура, которая распечатывает местоположение каждого экземпляра:

 s = "All the world's a stage. And all the men and women merely players";
 i = indexOf("the", s);
 while i != -1 {
   write(i);
   i =  indexOf("the", s, i+1);
 }

В Icon аналогом findявляется генератор, поэтому те же результаты можно получить с помощью одной строки:

 s := "All the world's a stage. And all the men and women merely players"
 every write(find("the", s))

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

 every write(5 < find("the", s))

Позиция будет возвращена, только если "the" появится после позиции 5; в противном случае сравнение не удастся, передайте ошибку на запись, и запись не произойдет.

everyОператор подобен while, цикл через каждый пункт , возвращаемый генератором и выход при отказе:

  every k := i to j do
   write(someFunction(k))

Существует разница между ключом everyи while; whileпереоценивает первый результат до тех пор, пока он не завершится ошибкой, а everyследующее значение получает от генератора. everyфактически вводит значения в функцию аналогично блокам в Smalltalk . Например, приведенный выше цикл можно переписать следующим образом:

 every write(someFunction(i to j))

В этом случае значения от i до j будут вводиться someFunctionи (потенциально) записывать несколько строк вывода.

Коллекции

Icon включает несколько типов коллекций, включая списки, которые также можно использовать как стопки и очереди , таблицы (также известные как карты или словари на других языках), наборы и другие. Иконка называет их структурами . Коллекции являются неотъемлемыми генераторами и могут быть легко вызваны с помощью синтаксиса Bang. Например:

 lines := []                    # create an empty list
 while line := read() do {      # loop reading lines from standard input
   push(lines, line)            # use stack-like syntax to push the line on the list
 }
 while line := pop(lines) do {  # loop while lines can be popped off the list
   write(line)                  # write the line out
 }

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

 lines := []                    # create an empty list
 while push(lines, read())      # push until empty
 while write(pop(lines))        # write until empty

Поскольку коллекция списков является генератором, это можно упростить с помощью синтаксиса bang:

 lines := []
 every push(lines, !&input)
 every write(!lines)

В этом случае удар writeзаставляет Icon возвращать строку текста одну за другой из массива и, наконец, терпит неудачу в конце. &inputявляется основанным на генераторе аналогом того, readчто считывает строку из стандартного ввода , поэтому !&inputпродолжает чтение строк до конца файла.

Поскольку Icon не имеет типа, списки могут содержать любые типы значений:

aCat := ["muffins", "tabby", 2002, 8]

Предметы могут включать в себя другие конструкции. Для создания более крупных списков Icon включает listгенератор; i := list(10, "word")формирует список, содержащий 10 копий слова. Как и массивы в других языках, Icon позволяет искать элементы по положению, например weight := aCat[4]. Включена нарезка массивов , позволяющая создавать новые списки из элементов других списков, например, aCat := Cats[2:4]создает новый список под названием aCat, который содержит слово «tabby », 2002 .

Таблицы - это, по сути, списки с произвольными индексными ключами, а не целыми числами:

 symbols := table(0)
 symbols["there"] := 1
 symbols["here"] := 2

Этот код создает таблицу, в которой в качестве значения по умолчанию для любого неизвестного ключа будет использоваться ноль. Затем он добавляет в таблицу два элемента с ключами «там» и «здесь» и значениями 1 и 2.

Наборы также похожи на списки, но содержат только один член любого заданного значения. Значок включает в ++себя объединение двух наборов, **пересечение и --разницу. Значок включает в себя ряд предопределенных "Cset", набор, содержащий различные символы. Есть четыре стандартных Csets в иконе, &ucase, &lcase, &letters, и &digits. Новый Csets можно сделать, приложив строку в одинарных кавычках, например, vowel := 'aeiou'.

Струны

В Icon строки - это списки символов. В виде списка они являются генераторами, поэтому их можно перебирать с помощью синтаксиса bang:

 write(!"Hello, world!")

Распечатает каждый символ строки в отдельной строке.

Подстроки можно извлечь из строки, используя спецификацию диапазона в скобках. Спецификация диапазона может возвращать точку на один символ или часть строки. Строки можно индексировать справа или слева. Позиции в строке определяются между символами 1 A 2 B 3 C 4 и могут быть указаны справа −3 A −2 B −1 C 0

Например,

 "Wikipedia"[1]     ==> "W"
 "Wikipedia"[3]     ==> "k"
 "Wikipedia"[0]     ==> "a"
 "Wikipedia"[1:3]   ==> "Wi"
 "Wikipedia"[-2:0]  ==> "ia"
 "Wikipedia"[2+:3]  ==> "iki"

Где в последнем примере показано использование длины вместо конечной позиции

Спецификацию нижних индексов можно использовать как lvalue в выражении. Это можно использовать для вставки строк в другую строку или удаления частей строки. Например:

    s := "abc"
    s[2] := "123"
    s now has a value of "a123c"
    s := "abcdefg"
    s[3:5] := "ABCD"
    s now has a value of "abABCDefg"
    s := "abcdefg"
    s[3:5] := ""
    s now has a value of "abefg"

Сканирование строк

Еще одним упрощением обработки строк является вызываемая с помощью система сканирования? , которая вызывает функции для строки:

 s ? write(find("the"))

Значок обращается к левой части объекта ?как к теме и передает его в строковые функции. Напомним, что findпринимает два параметра: текст поиска в качестве первого параметра и строку для поиска во втором параметре. Использование ?второго параметра является неявным и не требует указания программиста. В обычных случаях, когда несколько функций вызываются последовательно для одной строки, этот стиль может значительно уменьшить длину результирующего кода и улучшить ясность. Сигнатуры функции значка идентифицируют параметр объекта в своих определениях, поэтому параметр может быть поднят таким образом.

Это ?не просто синтаксический сахар, он также устанавливает «среду сканирования строк» ​​для любых последующих операций со строками. Это основано на двух внутренних переменных &subjectи &pos; &subjectпросто указатель на исходную строку, а &posтекущая позиция в ней или курсор. В различных процедурах обработки строк в Icon используются эти две переменные, поэтому программист не должен их явно указывать. Например:

  s := "this is a string"
  s ? write("subject=[",&subject,"], pos=[",&pos,"]")

произвел бы:

subject=[this is a string], pos=[1]

Для перемещения по сканируемой строке можно использовать встроенные и определяемые пользователем функции. Все встроенные функции будут по умолчанию &subjectи &posразрешить синтаксис сканирования для использования. Следующий код запишет все «слова», разделенные пробелами, в строку:

  s := "this is a string"
  s ? {                               # Establish string scanning environment
      while not pos(0) do  {          # Test for end of string
          tab(many(' '))              # Skip past any blanks
          word := tab(upto(' ') | 0)  # the next word is up to the next blank -or- the end of the line
          write(word)                 # write the word
      }
  }

В этом примере представлен ряд новых функций. posвозвращает текущее значение &pos. Может быть не сразу очевидно, зачем нужна эта функция, а не просто использовать значение &posнапрямую; причина в том, что &posэто переменная, и поэтому она не может принимать значение &fail, которое может принимать процедура pos. Таким образом, posобеспечивается легкая оболочка &pos, позволяющая легко использовать целенаправленное управление потоком Icon без необходимости выполнять написанные вручную логические тесты &pos. В этом случае тестом является «is & pos zero», что при нечетной нумерации расположения строк в Icon является концом строки. Если он не равен нулю, posвозвращается &fail, который инвертируется с, notи цикл продолжается.

manyнаходит один или несколько примеров предоставленного параметра Cset, начиная с текущего &pos. В этом случае он ищет пробелы, поэтому результатом этой функции является расположение первого непробельного символа после &pos. tabперемещается &posв это место, опять же с потенциалом &failна случай, например, manyпадения с конца струны. uptoпо сути является противоположностью many; он возвращает местоположение непосредственно перед предоставленным ему Cset, которое в примере затем устанавливает &posдля другого tab. Чередование также используется для остановки в конце строки.

Этот пример можно сделать более надежным за счет использования более подходящего Cset «разбиения на слова», который может включать точки, запятые и другие знаки препинания, а также другие символы пробелов, такие как табуляция и неразрывные пробелы. Затем этот Cset можно использовать в manyи upto.

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

 procedure main()
     s := "Mon Dec 8"
     s ? write(Mdate() | "not a valid date")
 end
 # Define a matching function that returns
 # a string that matches a day month dayofmonth
 procedure Mdate()
 # Define some initial values
 static dates
 static days
 initial {
        days := ["Mon","Tue","Wed","Thr","Fri","Sat","Sun"]
        months := ["Jan","Feb","Mar","Apr","May","Jun",
                  "Jul","Aug","Sep","Oct","Nov","Dec"]
 }
 every suspend   (retval <-  tab(match(!days)) ||     # Match a day
                             =" " ||                  # Followed by a blank
                             tab(match(!months)) ||   # Followed by the month
                             =" " ||                  # Followed by a blank
                             matchdigits(2)           # Followed by at least 2 digits
                 ) &
                 (=" " | pos(0) ) &                   # Either a blank or the end of the string
                 retval                               # And finally return the string
 end
 # Matching function that returns a string of n digits
 procedure matchdigits(n)
     suspend (v := tab(many(&digits)) & *v <= n) & v
 end

Критика

Лоуренс Тратт написал статью об Icon, в которой исследовал ее реальные приложения и указал на ряд проблемных областей. Среди них был ряд практических решений, которые исходят из обработки строк, но не имеют большого смысла в других областях. Из их:

Решение об отказе по умолчанию в конце процедур имеет смысл в контексте генераторов, но в меньшей степени в случае общих процедур. Возвращаясь к примеру, указанному выше, write(f(-1))не будет вывода, чего можно было ожидать. Тем не мение:

 x := 10
 (additional lines)
 x := f(-1)
 write(x)

приведет к печати 10. Проблема такого рода совсем не очевидна, так как даже в интерактивном отладчике весь код вызывается, но xникогда не получает ожидаемого значения. Это можно было бы отклонить как одну из тех "ловушек", о которых программисты должны знать на любом языке, но Тратт исследовал множество программ Icon и обнаружил, что подавляющее большинство процедур не являются генераторами. Это означает, что поведение Icon по умолчанию используется только крошечным меньшинством его конструкций, но представляет собой основной источник потенциальных ошибок во всех остальных.

Другой проблемой является отсутствие логического типа данных и обычной логической логики. Хотя система успеха / неудачи работает в большинстве случаев, когда конечной целью является проверка значения, это все же может привести к некоторому странному поведению в, казалось бы, простом коде:

 procedure main()
    if c then {
      write("taken")
    } 
 end

Эта программа напечатает «принято». Причина в том, что тест cдействительно возвращает значение; это значение равно нулю, значение по умолчанию для всех не инициированных в противном случае переменных. Ноль - допустимое значение, значит, if cуспешно. Чтобы решить эту проблему, необходимо сделать тест явным, c == 0что отвлекает от самодокументируемого кода - неоднозначно, проверяет ли это «с ноль» или «с существует».

Смотрите также

Примечания

использованная литература

Цитаты

Библиография

внешние ссылки