Разбор программы-парсер ini-файлов. Оригинал взят отсюда: "
Распарсить INI файл на K".
Код:
ini:0:`test.ini / записываем в текстовую переменную содержимое текстового файла test.ini
/ при этом в переменную пишется список строк, где каждому элементу соотв-т строка в текстовом файле
ini_tree:{r:(0 1) _ x; r[1]:{x[0]:(-1 _ x[0]); x}'{(0,1+x?"=") _ x}'r[1]; r}'(&{~~((1+x?"[")_ x)?"]"}'ini2) _ ini2:{x[&{~1>#x}'x]}[ini]
/ Разглядывать функции K нужно иерархически, по скобкам (круглым,
/ фигурным и квадратным), а внутри скобок нужно смотреть от конца к началу:
/ ini_tree: -- присваивание переменной ini_tree вычисляемого значения.
/ ini2:{x[&{~1>#x}'x]}[ini] -- смотрим этот кусок.
/ ini2: { ... }[ini] -- вычисление функции в фигурных скобках с аргументом ini
/ Обратите внимание что путём подстановки аргументом функции списка в языке K
/ делается цикл for each, с одновременным суммированием результата в каждой
/ итерации.
/ {~1>#x}'x -- цикл по строкам x (в ini)
/ ~1>#x -- выдаёт список логических значений (нулей или единиц), соотв-х больше ли длина строки чем 1 символ
/ &{ ~1>#x }'x -- вычисление индексов тех непустых (длина больше чем 1) строк.
/ x[ &{~1>#x}'x ] -- из списка x берём только непустые строки, то есть:
/ если x=("343" "343" "" "1"), то &{~1>#x}'x будет равно (0 1 3), и
/ будет считаться x[0 1 3], а это равно ("343" "343" "1")
/ Таким образом в ini2 у нас все непустые строки текстового файла test.ini
/ (&{~~((1+x?"[")_ x)?"]"}'ini2) _ ini2:{x[&{~1>#x}'x]}[ini] -- это бинарная операция _
/ Второй операнд мы только что разбирали. Его результат -- список непустых строк из
/ файла. Присваивание списка переменной ini2 тут побочным эффектом, присвоенное значение
/ идёт дальше.
/ (&{~~((1+x?"[")_ x)?"]"}'ini2) -- смотрим этот кусок.
/ ini2 -- берём значение переменной только что сохранённой побочным эффектом
/ { ... }'ini2 -- и опять применение безымянной подфункции к аргументу ini2 (то есть for each)
/ ~~((1+x?"[")_ x)?"]" -- здесь размечено скобками, первым будет:
/ 1+x?"[" -- выдаёт 1+положение первого символа "[" в строке x (которая ходит по строкам ini2)
/ ( 1+x?"[" )_ x -- выводит отрезок строки после первого символа "["
/ Обратите внимание что операция ? в случае если символа в строке не было, то он выводит не
/ -1, например, а длину строки, в которой был поиск. Поэтому для строк где не было символа
/ "[" кусок (1+x?"[")_ x даст пустую строку.
/ ((1+x?"[")_ x)?"]" -- даёт позицию символа "]" в отрезке строки после символа "["
/ ~~ ((1+x?"[")_ x)?"]" -- двумя логическими отрицаниями приводим числа к логическим 1/0
/ (& {~~((1+x?"[")_ x)?"]"}'ini2 ) -- операция & превращает набор логических значений в
/ список индексов чьи значения верны, то есть выдаёт номера строк заголовков ограниченных в
/ строке квадратными скобками.
/ Теперь возвращаемся к бинарному выражению:
/ (&{~~((1+x?"[")_ x)?"]"}'ini2) _ ini2:{x[&{~1>#x}'x]}[ini]
/ что тоже самое что и
/ (&{~~((1+x?"[")_ x)?"]"}'ini2) _ ini2
/ Так как результатом первого операнда становится номера строк-заголовков,
/ выражение выдаст нам списки строк *между* строками-заголовками.
/ Это надо допояснить:
/ 1_ 10 20 30 40 50 вычисляет 20 30 40
/ но 1 3 _ 10 20 30 40 50 выводит ( (20 30) (40 50) )
/ То есть операция _ делит правый операнд индексами из первого операнда,
/ и выводит разбитые куски, исключая первый:
/ 10 | 20 30 | 40 50
/ 1 3
/ Мы уже разделили ini файл по секциям, результат пока примерно такой:
/ ( ("[Section1]" "a=1" "b=2" "c=3") ("[Section2]" "b=3") )
/ Этот промежуточный результат операцией ' применяется к этой функции:
/ {r:(0 1) _ x; r[1]:{x[0]:(-1 _ x[0]); x}'{(0,1+x?"=") _ x}'r[1]; r}
/ Функция пробегает по каждой секции.
/ Операция ; является аналогом ЛИСПовской функции do, которая выполняет
/ свои аргументы, а результатом выдаёт результат вычисления последнего.
/ Поэтому вычисление этой функции состоит их последовательного вычисления
/ трёх операций разделённых точкой с запятой, последняя выводит результат.
/ r:(0 1) _ x -- присвоение списку r двух значений, r[0]=x[0], а r[1]=x[1...]
/ То для первого элемента из примера выше r будет равно
/ ( ("[Section1]") ("a=1" "b=2" "c=3") )
/ Первый элемент -- название секции, второй -- строки в секции.
/ r[1]:{x[0]:(-1 _ x[0]); x}'{(0,1+x?"=") _ x}'r[1]
/ {(0,1+x?"=") _ x}'r[1] -- цикл по строкам (второй элемент списка r) в секции
/ (0,1+x?"=") _ x -- каждую строчку делим на два отрезка: до символа "="
/ (с ним самим), и после него "=".
/ {x[0]:(-1 _ x[0]); x} применяется к уже разделённым строчкам:
/ Операция _ с отрицательным левым операндом делает тоже самое, только
/ индекс отсчитывается от конца списка, поэтому -1 _ x[0] уберёт последний
/ символ первого элемента списка x (а это будет "=" который надо убирать).
/ Третье дейтсвие после ; просто выводит временную r как результат.
/ Результаты для каждой секции объединяются.