Icon — это язык программирования очень высокого уровня, основанный на концепции «целенаправленного выполнения», в котором код возвращает «успех» вместе с допустимыми значениями или «неудачу», указывающую на отсутствие допустимых данных для возврата. Успешность и неуспешность данного блока кода используется для направления дальнейшей обработки, тогда как обычные языки обычно используют булеву логику, написанную программистом для достижения тех же целей. Поскольку логика для базовых структур управления часто неявна в Icon, общие задачи можно выполнять с помощью менее явного кода.
Icon был разработан Ральфом Грисволдом после ухода из Bell Labs , где он внес большой вклад в язык SNOBOL . SNOBOL был языком обработки строк с тем, что считалось устаревшим синтаксисом по стандартам начала 1970-х годов. После перехода в Университет Аризоны он продолжил развивать базовые концепции SNOBOL в SL5, но посчитал результат неудачным. Это привело к значительно обновленному Icon, который сочетает в себе короткий, но концептуально плотный код языков, подобных SNOBOL, с более знакомым синтаксисом языков, вдохновленных ALGOL, таких как C или Pascal .
Как и в языках, которые вдохновили его, основная область использования Icon — управление строками и текстовыми шаблонами. Операции со строками часто терпят неудачу, например, при поиске «the» в «world». В большинстве языков для этого требуется тестирование и ветвление, чтобы избежать использования недопустимого результата. В Icon большинство подобных тестов просто не нужны, что сокращает объем кода, который необходимо написать. Сложная обработка шаблонов может быть выполнена в нескольких строках краткого кода, похожего на более специализированные языки, такие как Perl , но сохраняющего более функционально-ориентированный синтаксис, знакомый пользователям других языков типа ALGOL.
Icon не является объектно-ориентированным , но объектно-ориентированное расширение под названием Idol было разработано в 1996 году, которое в конечном итоге стало Unicon . Он также вдохновил другие языки, причем его простые генераторы были особенно влиятельными; генераторы Icon были основным источником вдохновения для языка Python . [3]
Первоначальная работа SNOBOL , ретроспективно известная как SNOBOL1, была начата осенью 1962 года в отделе исследований программирования Bell Labs . [4] Эта работа была реакцией на разочарования, связанные с попытками использовать язык SCL для манипулирования полиномиальными формулами, символической интеграции и изучения цепей Маркова . SCL, написанный главой отдела Честером Ли, был медленным и имел низкоуровневый синтаксис, что приводило к объему кода даже для простых проектов. После краткого рассмотрения языка COMIT Иван Полонский, Ральф Грисволд и Дэвид Фарбер, все члены отдела из шести человек, решили написать свой собственный язык для решения этих проблем. [5]
Первые версии работали на IBM 7090 в начале 1963 года, а к лету были доработаны и использовались во всей Bell. Это почти сразу привело к SNOBOL2, который добавил ряд встроенных функций и возможность связываться с внешним кодом на языке ассемблера . Он был выпущен в апреле 1964 года и в основном использовался в Bell, но также нашел некоторое применение в Project MAC . Введение системных функций служило в основном для указания на необходимость пользовательских функций, что было главной особенностью SNOBOL3, выпущенного в июле 1964 года. [6]
Введение SNOBOL3 совпало с крупными изменениями в вычислительном отделе Bell Labs, включая добавление нового мэйнфрейма GE 645 , что потребовало бы переписывания SNOBOL. Вместо этого команда предложила написать новую версию, которая будет работать на виртуальной машине , названной SIL для SNOBOL Intermediate Language, что позволит легко переносить ее на любую достаточно мощную платформу. Это предложение было принято как SNOBOL4 в сентябре 1965 года. К этому времени в августе 1966 года появились планы значительно улучшенной версии языка. [7] Дальнейшая работа над языком продолжалась в течение оставшейся части 1960-х годов, в частности, был добавлен тип ассоциативного массива в более поздней версии, который они называли таблицей.
Гризволд покинул Bell Labs, чтобы стать профессором в Университете Аризоны в августе 1971 года. [8] В то время он представил SNOBOL4 как исследовательский инструмент. [9] Он получил гранты от Национального научного фонда для продолжения поддержки и развития SNOBOL. [10]
Как язык, изначально разработанный в начале 1960-х годов, синтаксис SNOBOL несет на себе следы других ранних языков программирования, таких как FORTRAN и COBOL . В частности, язык является столбцово-зависимым, поскольку многие из этих языков вводились на перфокартах , где столбцовая компоновка является естественной. Кроме того, структуры управления были почти полностью основаны на ветвлении вокруг кода, а не на использовании блоков , которые стали обязательной функцией после введения ALGOL 60. К тому времени, как он переехал в Аризону, синтаксис SNOBOL4 безнадежно устарел. [11]
Гризволд начал работу по внедрению базовой концепции успеха/неудачи SNOBOL с традиционными структурами управления потоком, такими как if/then. Это стало SL5, сокращением от "SNOBOL Language 5", но результат оказался неудовлетворительным. [11] В 1977 году он вернулся к языку, чтобы рассмотреть новую версию. Он отказался от очень мощной системы функций, представленной в SL5, с более простой концепцией приостановки/возобновления и разработал новую концепцию для естественного преемника SNOBOL4 со следующими принципами; [11]
Новый язык изначально был известен как SNOBOL5, но поскольку он значительно отличался от SNOBOL во всем, кроме базовой концепции, в конечном итоге было желательным новое название. После рассмотрения «s» как своего рода дань уважения «C», но в конечном итоге от этого отказались из-за проблем с набором документов, использующих это название. Был предложен ряд новых названий, которые были отклонены; Irving, bard и «TL» для «The Language». Именно в это время Xerox PARC начал публиковать информацию о своей работе над графическими пользовательскими интерфейсами , и термин «значок» начал входить в компьютерный лексикон. Было принято решение изменить название сначала на «значок», прежде чем окончательно выбрать «Значок». [11] [a]
Язык Icon происходит от класса структурных языков программирования ALGOL и, таким образом, имеет синтаксис, похожий на C или Pascal . Icon больше всего похож на Pascal, используя синтаксис для присваиваний, ключевое слово и похожий синтаксис. С другой стороны, Icon использует фигурные скобки в стиле C для структурирования групп выполнения, а программы начинаются с запуска процедуры, называемой . [13]:=
procedure
main
Во многих отношениях Icon также разделяет черты большинства языков сценариев (а также SNOBOL и SL5, из которых они были взяты): переменные не нужно объявлять, типы приводятся автоматически, а числа могут быть преобразованы в строки и обратно автоматически. [14] Еще одной чертой, общей для многих языков сценариев, но не для всех, является отсутствие символа конца строки; в Icon строки, которые не заканчиваются точкой с запятой, заканчиваются подразумеваемой точкой с запятой, если это имеет смысл. [15]
Процедуры являются основными строительными блоками программ Icon. Хотя они используют именование Pascal, они работают скорее как функции C и могут возвращать значения; в Icon нет function
ключевого слова. [16]
Одной из ключевых концепций SNOBOL было то, что его функции возвращали «успех» или «неудачу» как примитивы языка, а не использовали магические числа или другие методы. [17] [18] Например, функция, которая возвращает позицию подстроки в другой строке, является обычной процедурой, встречающейся в большинстве языковых систем выполнения ; в JavaScript может потребоваться найти позицию слова «World» в программе «Hello, World!» , что будет сделано с помощью , которая вернет 7. Если вместо этого запросить , код «потерпит неудачу», так как искомый термин не появляется в строке. В JavaScript, как и в большинстве языков, это будет указано путем возврата магического числа, в данном случае -1. [19]position = "Hello, World".indexOf("World")
position = "Hello, World".indexOf("Goodbye")
В SNOBOL неудача такого рода возвращает особое значение, fail
. Синтаксис SNOBOL работает непосредственно с успехом или неудачей операции, переходя к помеченным разделам кода без необходимости писать отдельный тест. Например, следующий код печатает "Hello, world!" пять раз: [20]
* Программа SNOBOL для печати Hello World I = 1 LOOP OUTPUT = "Hello, world!" I = I + 1 LE ( I , 5 ) : S ( LOOP ) END
LE
Для выполнения цикла вызывается оператор «меньше или равно» для индексной переменной I, и если он S
успешен, то есть I меньше 5, он переходит к указанной метке LOOP
и продолжается. [20]
Icon сохранил концепцию управления потоком на основе успеха или неудачи, но развил язык дальше. Одним из изменений стала замена маркированного GOTO
-подобного ветвления на блочно-ориентированные структуры в соответствии со стилем структурного программирования , который охватил компьютерную индустрию в конце 1960-х годов. [11] Вторым было разрешение передавать «неудачу» по цепочке вызовов, чтобы целые блоки были успешными или неудачными как единое целое. Это ключевая концепция языка Icon. В то время как в традиционных языках пришлось бы включать код для проверки успеха или неудачи на основе булевой логики , а затем выполнять переход на основе результата, такие тесты и переходы присущи коду Icon и не должны быть явно прописаны. [21]
Например, рассмотрим этот фрагмент кода, написанный на языке программирования Java . Он вызывает функцию read()
для чтения символа из (ранее открытого) файла, присваивает результат переменной a
, а затем write
s значение a
в другой файл. Результатом является копирование одного файла в другой. read
в конечном итоге закончатся символы для чтения из файла, возможно, при самом первом вызове, что оставит a
в неопределенном состоянии и потенциально приведет write
к возникновению исключения нулевого указателя . Чтобы избежать этого, read
возвращает специальное значение EOF
(конец файла) в этой ситуации, что требует явной проверки для write
его избежания:
в то время как (( a = read ()) != EOF ) { write ( a ); }
Напротив, в Icon read()
функция возвращает строку текста или &fail
. &fail
не является просто аналогом EOF
, поскольку это явно понимается языком как «остановить обработку» или «выполнить случай сбоя» в зависимости от контекста. Эквивалентный код в Icon: [18]
Это означает, «пока чтение не завершается неудачей, вызывать запись, в противном случае останавливаться». [18] Нет необходимости указывать тест на магическое число, как в примере Java, это подразумевается, и полученный код упрощается. Поскольку успех и неудача передаются вверх по цепочке вызовов, можно встраивать вызовы функций в другие, и они останавливаются, когда вложенный вызов функции завершается неудачей. Например, код выше можно сократить до: [22]
В этой версии, если read
вызов терпит неудачу, write
вызов терпит неудачу, и while
останавливается. [22] Конструкции ветвления и цикла Icon основаны на успехе или неудаче кода внутри них, а не на произвольном булевом тесте, предоставленном программистом. if
выполняет then
блок, если его «тест» возвращает значение, и выполняет else
блок или переходит к следующей строке, если он возвращает &fail
. Аналогично, while
продолжает вызывать свой блок, пока не получит отказ. Icon называет эту концепцию целенаправленным выполнением . [23]
Важно противопоставить концепции успеха и неудачи концепции исключения ; исключения — это необычные ситуации, а не ожидаемые результаты. Неудачи в Icon — это ожидаемые результаты; достижение конца файла — это ожидаемая ситуация, а не исключение. Icon не имеет обработки исключений в традиционном смысле, хотя fail часто используется в ситуациях, подобных исключениям. Например, если считываемый файл не существует, read
происходит сбой без указания особой ситуации. [18] В традиционном языке эти «другие условия» не имеют естественного способа указания; могут использоваться дополнительные магические числа, но более типично обработка исключений используется для «выдачи» значения. Например, для обработки отсутствующего файла в коде Java можно увидеть:
try { while (( a = read ()) != EOF ) { write ( a ); } } catch ( Exception e ) { // что-то еще пошло не так, используйте этот catch для выхода из цикла }
В этом случае необходимо два сравнения: одно для EOF и другое для всех остальных ошибок. Поскольку Java не позволяет сравнивать исключения как логические элементы, как в Icon, try/catch
вместо этого следует использовать длинный синтаксис. Блоки try также налагают штраф производительности, даже если не выбрасывается исключение, распределенные издержки , которых Icon обычно избегает.
Icon использует тот же целевой механизм для выполнения традиционных булевых тестов, хотя и с тонкими различиями. Простое сравнение типа не означает «если оценка условного выражения приводит к или возвращает истинное значение», как это было бы в большинстве языков; вместо этого оно означает что-то вроде «если условное выражение, здесь операция, успешно и не терпит неудачу». В этом случае оператор успешно выполняется, если сравнение истинно. Вызывает свое предложение, если выражение успешно, или или следующую строку, если оно терпит неудачу. Результат похож на традиционный if/then, который можно увидеть в других языках, выполняет if is less than . Тонкость в том, что одно и то же выражение сравнения можно разместить где угодно, например:if a < b then write("a is smaller than b")
<
<
if
then
else
if
then
a
b
Другое отличие заключается в том, что <
оператор возвращает свой второй аргумент, если он успешен, что в этом примере приведет к значению, которое b
будет записано, если оно больше, чем a
, в противном случае ничего не будет записано. Поскольку это не проверка как таковая , а оператор, который возвращает значение, их можно связать вместе, позволяя делать такие вещи, как if a < b < c
, [22] распространенный тип сравнения, который в большинстве языков должен быть записан как конъюнкция двух неравенств, таких как if (a < b) && (b < c)
.
Ключевым аспектом целенаправленного выполнения является то, что программе может потребоваться вернуться к более раннему состоянию, если процедура завершается неудачей, задача, известная как возврат назад . Например, рассмотрим код, который устанавливает переменную в начальное положение, а затем выполняет операции, которые могут изменить значение — это распространено, например, в операциях сканирования строк, которые будут продвигать курсор по строке по мере сканирования. Если процедура завершается неудачей, важно, чтобы любые последующие чтения этой переменной возвращали исходное состояние, а не состояние, в котором она была внутренне обработана. Для этой задачи Icon имеет обратимый оператор присваивания<-
, и обратимый обмен ,. <->
Например, рассмотрим некоторый код, который пытается найти строку шаблона в большей строке:
Этот код начинается с перемещения i
к 10, начальной точке поиска. Однако, если find
не удается, блок не удастся в целом, что приводит к тому, что значение i
остается равным 10 в качестве нежелательного побочного эффекта . Замена i := 10
на i <- 10
указывает, что i
следует сбросить до предыдущего значения, если блок не удается. Это обеспечивает аналог атомарности при выполнении.
Выражения в Icon могут возвращать одно значение, например, 5 > x
будут оценивать и возвращать x, если значение x меньше 5, или в противном случае завершаться неудачей. Однако Icon также включает концепцию процедур, которые не возвращают немедленно успех или неудачу, а вместо этого возвращают новые значения каждый раз, когда они вызываются. Они известны как генераторы и являются ключевой частью языка Icon. На языке Icon оценка выражения или функции создает последовательность результатов . Последовательность результатов содержит все возможные значения, которые могут быть сгенерированы выражением или функцией. Когда последовательность результатов исчерпана, выражение или функция завершается неудачей.
Значок позволяет любой процедуре возвращать одно или несколько значений, контролируемых с помощью ключевых слов fail
, return
и suspend
. Процедура, в которой отсутствует любое из этих ключевых слов, возвращает &fail
, что происходит всякий раз, когда выполнение доходит до end
процедуры. Например:
Вызов f(5)
вернет 1, но вызов f(-1)
вернет &fail
. Это может привести к неочевидному поведению, например, write(f(-1))
ничего не выведет, поскольку f
завершится неудачей и приостановит работу write
. [24]
Преобразование процедуры в генератор использует suspend
ключевое слово, которое означает «возвратить это значение и при повторном вызове начать выполнение с этой точки». В этом отношении это что-то вроде комбинации концепций static
в C и return
. Например: [18]
создает генератор, который возвращает ряд чисел, начинающихся с i
и заканчивающихся a j
, а затем возвращается &fail
после этого. [b] Останавливает suspend i
выполнение и возвращает значение i
без сброса какого-либо состояния. Когда делается еще один вызов той же функции, выполнение возобновляется в этой точке с предыдущими значениями. В этом случае это заставляет его выполнить i +:= 1
, вернуться к началу блока while, а затем вернуть следующее значение и снова приостановиться. Это продолжается до тех пор , пока i <= j
не произойдет сбой, после чего он выходит из блока и вызывает fail
. Это позволяет легко создавать итераторы . [18]
Другой тип генератора-конструктора — это генератор переменного тока , который выглядит и работает как логический or
оператор. Например:
Кажется, что это означает «если y меньше x или 5, то...», но на самом деле это краткая форма для генератора, который возвращает значения, пока не выпадет из конца списка. Значения списка «внедряются» в операции, в данном случае, <
. Таким образом, в этом примере система сначала проверяет y < x, если x действительно больше y, она возвращает значение x, тест проходит, и значение y записывается в then
предложение. Однако, если x не больше y, он терпит неудачу, и генератор продолжает выполнять y < 5. Если этот тест проходит, y записывается. Если y меньше ни x, ни 5, генератор исчерпывает тесты и терпит неудачу, терпит if
неудачу, и write
не выполняется. Таким образом, значение y появится на консоли, если оно меньше x или 5, тем самым выполняя цель булевой функции or
. Функции не будут вызываться, если оценка их параметров не будет успешной, поэтому этот пример можно сократить до:
Внутри генератор не просто or
и его можно использовать для построения произвольных списков значений. Это можно использовать для итерации произвольных значений, например:
Поскольку списки целых чисел часто встречаются во многих контекстах программирования, Icon также включает to
ключевое слово для создания специальных генераторов целых чисел:
что можно сократить:
Значок не имеет строгой типизации, поэтому списки генераторов могут содержать различные типы элементов:
Это запишет 1, «привет» и, возможно, 5 в зависимости от значения x.
Аналогично оператор конъюнкции , &
, используется аналогично булевскому and
оператору: [25]
Этот код вызывает ItoJ
и возвращает начальное значение 0, которое присваивается x. Затем он выполняет правую часть конъюнкции, и поскольку x % 2
do равно 0, он выводит значение. Затем он ItoJ
снова вызывает генератор, который присваивает 1 x, что приводит к ошибке правой части и ничего не печатает. Результатом является список всех четных целых чисел от 0 до 10. [25]
Концепция генераторов особенно полезна и эффективна при использовании со строковыми операциями и является основной основой для общего дизайна Icon. Рассмотрим indexOf
операцию, встречающуюся во многих языках; эта функция ищет одну строку в другой и возвращает индекс ее местоположения или магическое число, если она не найдена. Например:
s = "Весь мир - театр. И все мужчины и женщины - просто игроки" ; i = indexOf ( "the" , s ); write ( i );
Это просканирует строку s
, найдет первое вхождение «the» и вернет этот индекс, в данном случае 4. Однако строка содержит два экземпляра строки «the», поэтому для возврата второго примера используется альтернативный синтаксис:
j = indexOf ( "the" , s , i + 1 ); запись ( j );
Это говорит ему начать сканирование с позиции 5, поэтому оно не будет соответствовать первому экземпляру, который мы нашли ранее. Однако может не быть второго экземпляра "the" - может не быть и первого - поэтому возвращаемое значение из indexOf
должно быть проверено на магическое число -1, которое используется для указания на отсутствие совпадений. Полная процедура, которая выводит местоположение каждого экземпляра, выглядит так:
s = "Весь мир - театр. И все мужчины и женщины - просто игроки" ; i = indexOf ( "the" , s ); while i != - 1 { write ( i ); i = indexOf ( "the" , s , i + 1 ); }
В Icon эквивалентом find
является генератор, поэтому те же результаты можно создать с помощью одной строки:
Конечно, бывают случаи, когда нужно найти строку после некоторой точки ввода, например, при сканировании текстового файла, содержащего номер строки в первых четырех столбцах, пробел и затем строку текста. Целенаправленное выполнение можно использовать для пропуска номеров строк:
Позиция будет возвращена только в том случае, если «the» появляется после позиции 5; в противном случае сравнение завершится неудачей, произойдет сбой записи, и запись не будет выполнена.
Оператор every
похож на while
, проходя по каждому элементу, возвращаемому генератором, и завершая работу в случае возникновения ошибки: [24]
every
Между и есть ключевое различие while
: while
переоценивает первый результат до тех пор, пока он не даст сбой, тогда как every
извлекает следующее значение из генератора. every
фактически вводит значения в функцию способом, похожим на блоки в Smalltalk . Например, приведенный выше цикл можно переписать следующим образом: [24]
В этом случае значения от i до j будут введены someFunction
и (потенциально) записаны в несколько строк вывода. [24]
Icon включает несколько типов коллекций , включая списки , которые также могут использоваться как стеки и очереди , таблицы (также известные как карты или словари в других языках), наборы и другие. Icon называет их структурами . Коллекции являются встроенными генераторами и могут быть легко вызваны с помощью синтаксиса bang. Например:
Используя распространение сбоев, как показано в предыдущих примерах, мы можем объединить тесты и циклы:
Поскольку коллекция списков является генератором, это можно еще больше упростить с помощью синтаксиса bang:
В этом случае bang in write
заставляет Icon возвращать строки текста одну за другой из массива и в конце завершаться ошибкой. &input
— это аналог на основе генератора , read
который считывает строку из стандартного ввода , поэтому !&input
продолжает считывать строки до тех пор, пока файл не закончится.
Поскольку Icon не имеет типа, списки могут содержать любые различные типы значений:
Элементы могут включать другие структуры. Для создания более крупных списков Icon включает list
генератор; i := list(10, "word")
генерирует список, содержащий 10 копий "word". Как и массивы в других языках, Icon позволяет искать элементы по позиции, например, weight := aCat[4]
. Включена нарезка массива , что позволяет создавать новые списки из элементов других списков, например, aCat := Cats[2:4]
создает новый список с именем aCat, содержащий "tabby" и 2002.
Таблицы по сути представляют собой списки с произвольными ключами индексов, а не целыми числами:
Этот код создает таблицу, которая будет использовать ноль в качестве значения по умолчанию для любого неизвестного ключа. Затем он добавляет два элемента в таблицу с ключами "there" и "here" и значениями 1 и 2.
Наборы также похожи на списки, но содержат только один элемент любого заданного значения. Icon включает в себя ++
для создания объединения двух наборов, **
пересечения и --
разности. Icon включает в себя ряд предопределенных "Cset", наборов, содержащих различные символы. В Icon есть четыре стандартных Cset, &ucase
, &lcase
, &letters
и &digits
. Новые Cset можно создать, заключив строку в одинарные кавычки, например, vowel := 'aeiou'
.
В Icon строки — это списки символов. Как список, они являются генераторами и, таким образом, могут быть итерированы с использованием синтаксиса bang:
Каждый символ строки будет выведен на отдельную строку.
Подстроки могут быть извлечены из строки с помощью спецификации диапазона в скобках. Спецификация диапазона может возвращать точку на один символ или часть строки. Строки могут быть индексированы как справа, так и слева. Позиции внутри строки определяются как находящиеся между символами 1 A 2 B 3 C 4 и могут быть указаны справа −3 A −2 B −1 C 0
Например,
Где последний пример показывает использование длины вместо конечной позиции
Спецификация индексации может использоваться как lvalue внутри выражения. Это может использоваться для вставки строк в другую строку или удаления частей строки. Например:
Дальнейшее упрощение обработки строк — это система сканирования , вызываемая с помощью ?
, которая вызывает функции для строки:
Icon ссылается на левую часть ?
как на субъект и передает его в строковые функции. Напомним, что find
принимает два параметра: текст поиска как параметр один и строку для поиска как параметр два. Использование ?
второго параметра подразумевается и не должно быть указано программистом. В обычных случаях, когда несколько функций вызываются последовательно для одной строки, этот стиль может значительно сократить длину результирующего кода и улучшить ясность. Сигнатуры функций Icon идентифицируют параметр субъекта в своих определениях, поэтому параметр может быть поднят таким образом.
Это ?
не просто форма синтаксического сахара, он также устанавливает «среду сканирования строк» для любых последующих операций со строками. Это основано на двух внутренних переменных, &subject
и &pos
; &subject
— это просто указатель на исходную строку, а &pos
— текущая позиция внутри нее, или курсор. Различные процедуры обработки строк Icon используют эти две переменные, поэтому они не должны явно указываться программистом. Например:
будет производить:
субъект=[это строка], pos=[1]
Встроенные и определяемые пользователем функции могут использоваться для перемещения по сканируемой строке. Все встроенные функции будут по умолчанию &subject
и &pos
позволят использовать синтаксис сканирования. Следующий код запишет все разделенные пробелами "слова" в строку:
В этом примере представлено несколько новых функций. 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
.
Более сложный пример демонстрирует интеграцию генераторов и сканирования строк в язык.
Лоренс Тратт написал статью о Icon, в которой исследовал его реальные приложения и указал на ряд проблемных областей. Среди них было несколько практических решений, которые вытекают из их происхождения в обработке строк, но не имеют особого смысла в других областях. [24] Среди них:
Решение о сбое по умолчанию в конце процедур имеет смысл в контексте генераторов, но в меньшей степени в случае общих процедур. Возвращаясь к примеру, отмеченному выше, write(f(-1))
не выведет то, что можно было бы ожидать. Однако: [24]
приведет к печати 10. Такого рода проблемы совсем не очевидны, так как даже в интерактивном отладчике весь код вызывается, но x
никогда не получает ожидаемого значения. Это можно было бы отбросить как один из тех "подводных камней", о которых программисты должны знать в любом языке, но Тратт исследовал множество программ Icon и обнаружил, что подавляющее большинство процедур не являются генераторами. Это означает, что поведение Icon по умолчанию используется только крошечным меньшинством его конструкций, но представляет собой основной источник потенциальных ошибок во всех остальных. [24]
Другая проблема — отсутствие типа данных Boolean [c] и обычной булевой логики. Хотя система успеха/неудачи работает в большинстве случаев, где конечной целью является проверка значения, это все равно может привести к странному поведению в, казалось бы, простом коде: [25]
Эта программа выведет "taken". Причина в том, что тест, c
, возвращает значение; это значение равно &null
, значению по умолчанию для всех иных не инициированных переменных. [26] &null
является допустимым значением, поэтому if c
успешно. Чтобы проверить это, нужно сделать тест явным, c === &null
. Тратт предположил, что это отвлекает от самодокументируемого кода, ошибочно предположив, что он проверяет "равно ли c нулю" или "существует ли c". [25]
fail
требуется , так как он стоит непосредственно перед end
. Он был добавлен для ясности.