четверг, 5 августа 2021 г.

Как собрать для блога HTML с подсветкой кода из Markdown

TL;DR: Как запостить Markdown пост с примерами кода (и картинками) в бложик, поддерживающий только HTML

# install using pip
pip install -U markdown Pygments
# or (on Debian based)
sudo apt install python3-markdown python3-pygments

edit post.md
(echo '<style>';
    pygmentize -S native -f html -a .codehilite;
    echo '</style>';
    markdown_py post.md -x codehilite -x fenced_code
) > post.html

Вчера написал пост с заметками по ходу решения задач к Coq'Art. Начал я его писать как короткий комментарий в локальный tg чат с друзьями. Но сначала понял, что хочется поделиться на большую аудиторию (может кто прокомментирует чего полезного), поэтому решил запостить в point.im или juick.com в которые изредка что-то пишу, а потом, пока писал, обнаружил, что размер поста уже больше подходит для полноценного поста в бложек. На этом месте я вспомнил что у меня даже был такой).

Размечал текст я по привычке в Markdown - это очень простой, привычный мне формат, и он легко переделывается в другие вики разметки, поэтому я в нём часто пишу даже заметки рядом с кодом и пр. Ну и про поддержку в GitHub не забываем. Когда же я открыл blogspot, то обнаружил, что там только два варианта ввода разметки: HTML и их редактор. Поэтому пришлось вспоминать, как собирать из Markdown файлов HTML.

Припоминалось, что я уже этот вопрос как-то исследовал, но никаких заметок или следов этого обнаружить не удалось. Видимо слишком давно это было. Однако удалось вспомнить, что в прошлый раз я остановился на Python-Markdown. К тому же он уже оказался установлен (варианты см. в начале статьи). Так что решил попробовать по-быстрому заюзать его, и только если что-то не получится, то уже смотреть другие варианты.

Простого вызова markdown_py post.md -f post.html уже бы хватило, если бы в посте не было вставок с кодом. Но т.к. они были, то сначала обнаружилось, что блоки в бэктиках

```

```

это нестандартная фича, и весь код у меня слился с текстом.

screen0

Этот и следующий скриншоты - из Firefox'а в reader view, чтобы лишний раз в blogspot не лезть, но и чтобы от белого фона глаза не вытекали.

Картинки, к слову, в Markdown тоже можно вставлять, но надо сначала их куда-то захостить. Пока ручками сложил в Dropbox и воспользовался рецептом (поменять в публичной ссылке ?dl=0 на ?raw=1), чтобы получить прямую ссылку на картинку. Надо попробовать перехостить всё это дело на GitHub Pages - там такой проблемы быть не должно, да и с форматированием меньше возни. Или как-нибудь автоматизировать заливку картинок.

Чтобы поправить отображение блоков с кодом, надо подключить (идущее в комплекте) расширение Fenced Code Blocks.

И, конечно, сходу нет никакой подсветки для блоков с кодом, поэтому надо ещё подключить расширение CodeHilite, которое тоже идёт в коробке, но зависит от библиотеки Pygments, которую надо ставить отдельно.

Т.о. получилась такая команда:

markdown_py post.md -x codehilite -x fenced_code -f post.html

screen1

Выглядит уже прилично, но код всё ещё не раскрашен. Это потому, что надо сгенерировать стили для Pygmets. Об этом написано в инструкции к CodeHilite:

pygmentize -S default -f html -a .codehilite > styles.css

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

(echo '<style>'; cat styles.css; echo '</style>';
    markdown_py post.md -x codehilite -x fenced_code) > post.html

Уже совсем хорошо. Но когда запостил в blogspot, то понял, что фон явно указан белый.

screen2

Это уже скрин с blogspot, чтобы была понятна проблема.

Поглядел другие темы и выбрал native. Окончательное решение одной командой приведено в начале статьи.

Выглядит так:

screen3

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

img {
  border: 1px solid darkgrey;
  width: 400px;
}

Стиль рамки, правда в итоге чем-то переопределяется, ну да и чёрт с ним.

В целом, для первого подхода, этого должно бы уже хватить. Но я подумал, что стоит записать команды на память, чтобы для последующих постов, если такие будут, не вспомнить или не выискивать их в истории команд. А лучшая заметка - это исполняемый код! Соответственно рассмотрел два варианта: написать скрипт или пихнуть нужные команды в Makefile. Т.к. если постов будет несколько, то прописывать каждый раз при генерации пути ко входу и выходу лень, и к тому же тут уже несколько ресурсов, то решил остановиться на втором варианте.

Сразу обнаружил, что мейком то я пользоваться, оказывается толком не умею. И пришлось долго читать ман и интернеты прежде чем у меня получилось его заставить выполнять одну и ту же команду на каждой паре *.md -> *.html, но в итоге я написал вот такой Makefile:

# задаём цель по умолчанию
default: all
# следующие цели - это не файлы
.PHONY: all clean list-md-files list-html-files

# получаем список Markdown файлов с помощью функции wildcard
# см. https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html
# '::=' - это полустандартное присваивание нерекурсивной переменной
# см. https://www.gnu.org/software/make/manual/html_node/Flavors.html
MD_FILES ::= $(wildcard *.md)

# Строим по списку md файлов список html файлов.
# Будем складывать их в ./out
# patsubst делает замену по шаблону
# см. https://www.gnu.org/software/make/manual/html_node/Text-Functions.html
HTML_FILES ::= $(patsubst %.md,out/%.html,$(MD_FILES))

# команды для диагностики
list-md-files:
    @echo $(MD_FILES)

list-html-files:
    @echo $(HTML_FILES)

# т.к. clean удаляет папку ./out полностью, то надо её пересоздавать 
out:
    mkdir -p out

# стили для раскраски 
out/pygments.css: out
    pygmentize -S native -f html -a .codehilite > out/pygments.css

# Самая магия. Не разобрался, как использовать тут MD_FILES и HTML_FILES,
# чтобы для каждой пары md и html генерилось отдельное правило,
# поэтому воспользовался шаблонами с %:
# https://www.gnu.org/software/make/manual/html_node/Pattern-Intro.html
# '$<' и '$@' - это "автоматические переменные"
# https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
out/%.html: %.md out styles.css out/pygments.css
    ( echo '<style>'; cat styles.css out/pygments.css; echo '</style>'; \
        markdown_py $< -x codehilite -x fenced_code ) > $@

# в текущей версии MD_FILES и HTML_FILES нужны только для того,
# чтобы подвязать все цели по генерации html в all
all: $(HTML_FILES)

# чтобы точно не оставлять мусора (при удалении md файлов например)
# удаляем папку ./out целиком
clean:
    rm -rfv out

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

среда, 4 августа 2021 г.

Coq'Art. Заметки на тему. Ex 6.47 Trees with fixed height

Читаю Coq'Art. Доказал только что очередной (достаточно очевидный) факт, и внезапно захотелось поделиться и успехом (это было не очень просто), и мыслями на тему. А за одно решил обновить бложек - кучу лет сюда не заходил, а тут такой лонгрид получился.

Решал задание 6.47: написать функцию, которая строит бинарное дерево заполненное попарно различными натуральными числами. В этом задании используется тип не просто бинарного дерева, а бинарного дерева строго определённой высоты, т.е. высота - это параметр типа, и все листья лежат строго на одной глубине:

Inductive htree (X : Type) : nat -> Type
  := hleaf : X -> htree X 0
   | hnode : forall h : nat, X -> htree X h -> htree X h -> htree X (S h).

Моё решение - нумерация "ахнентафелем". Это способ индексации, используемый для хранения бинарной кучи в массиве, что то же самое, что индексация по порядку обхода в ширину, но об этом ниже.

Fixpoint pn_tree_aux (h n : nat) {struct h} : htree nat h
  := match h with
     | O => hleaf n
     | S h' => hnode h' n (pn_tree_aux h' (2*n + 1))
                          (pn_tree_aux h' (2*n + 2))
     end.

Definition pn_tree (h : nat) : htree nat h := pn_tree_aux h 0.

А авторское - нумерация по обходу в глубину.

Fixpoint make_htree_aux (h: nat) (start : Z) : Z * htree Z h :=
match h with
  | 0 => ((1+ start)%Z, hleaf start)
  | S h' => let p := make_htree_aux h' (1+start)%Z 
            in let q := make_htree_aux h'  (fst p) 
               in (fst q, hnode start (snd p) (snd q))
end.

Definition make_htree (n:nat) : htree Z n := snd (make_htree_aux n 0%Z).

Я стараюсь про такие задания кроме самого решения ещё доказывать, что оно возвращает то, что нужно. В данном случае я решил доказать сначала, что все значения в получившихся деревьях попарно различны, а потом более сильное, что если разложить эти деревья одним из обходов в список, то получится интервал натуральных чисел от 0 до (2^(h+1) - 2). Факты, казалось бы, тривиальные, а попробуй докажи. Особенно много сложностей возникло с моим решением.

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

2 <= 2 ^ h * (2 * n + 1 + 2)

А вот такое требует минут 5-10 размышлений и пяти довольно длинных строчек на доказательство:

S (2^(S h) - 1 + (2^(S h) - 1)) = 2 * (2^h + (2^h + 0)) - 1

Подбешивает, кстати, что тактика simpl разворачивает умножение на константу слева. Т.е. если написано 2*n, то после simpl будет n + (n + 0). Не знаю, как это запретить. Поэтому сделал теорему, которая переписывает выражение обратно. Но её приходится регулярно вызывать, чтобы можно было прочитать, что там теперь в goal.

А с доказательством, что уплощение построенного дерева образует интервал натуральных чисел проблемы начались уже с того, что для htree (дерева фиксированной высоты) нельзя просто так взять и написать обход в ширину, как для обычного. Пример для обычного дерева с Rosetta Code:

Fixpoint levelorder_forest (fuel: nat) (f: list tree) : list nat :=
  match fuel with
  | O => nil
  | S fuel'
    => let g := (fun t r => let '(x, f) := r 
                        in (value t :: x, children t ++ f))
      in let '(p, f) := fold_right g (nil, nil) f
      in p ++ levelorder_forest fuel' f
  end.

Definition levelorder (t: tree) : list nat
  := levelorder_forest (height t) (t :: nil).

Моя попытка повторить (все использованные тут определения можно найти тут же):

Definition flat_htree_bfs_aux1 {X : Type} (h : nat)
           (t : htree X (S h)) (acc : list (htree X h))
  := (htree_t1 t) :: (htree_t2 t) :: acc.

Fixpoint flat_htree_bfs_aux {X : Type} {h : nat}
         (q : list (htree X h)) {struct h}
  : list X
  := match h with
     | O => map htree_x q
     | S h' => let q' := fold_right (flat_htree_bfs_aux1 h')
                                    [] q in  (* <--- error *)
              let cur_layer := map htree_x q in
              let next_layers := (flat_htree_bfs_aux q') in
              cur_layer ++ next_layers
     end.

Definition flat_htree_bfs {X : Type} {h : nat} (t : htree X h) : list X
  := @flat_htree_bfs_aux X h [t].

Выдаёт ошибку "The term "q" has type "list (htree X h)" while it is expected to have type "list (htree X (S h'))" (cannot unify "h" and "S h'").". Т.е. coq не понимает, что S h' = h, хотя казалось бы должен - мы же тут как раз деструктурируем h на S h'.

Как оказалось это известная проблема, которая даже описана в книгах, а у воркэраунда даже есть собственное название "convoy pattern". Этот паттерн предполагает замену типа всего match-выражения на эквивалентное, но возвращающее функцию, которая в свою очередь принимает как аргумент проблемное значение (в моём случае q), и возвращает то что нужно. Т.е. нужно переписать так:

Fixpoint flat_htree_bfs_aux {X : Type} {h : nat}
         (q : list (htree X h)) {struct h}
  : list X
  := match h return list (htree X h) -> list X with
     | O => fun Q => map htree_x Q
     | S h' => fun Q => let q' := fold_right (flat_htree_bfs_aux1 h') [] Q
                    in let cur_layer := map htree_x Q
                    in let next_layers := (flat_htree_bfs_aux q')
                    in cur_layer ++ next_layers
     end q.

Почему без этого не работает, а с этим работает, я пока не очень понял, но надеюсь ещё пойму.

А дальше, конечно же возникла целая пачка сложностей в работе со столь громоздким определением обхода (и снова со степенями и минусами). Особенно всё усложняло то, что simpl часто разворачивал цели в слишком большую портянку, а попытка запретить ему некоторые преобразования, связанные с паттернматчингом по (S h') (высота дерева + 1. естественным образом постоянно возникает при доказательствах по индукции) выносом его в контекст (remember (S h') as h eqn:eq_h) часто приводили к ошибкам из-за того, что где-то в этом же выражении есть дерево высоты h, и получается выражение вроде такого (упрощённо) htree X h1 = htree X (S h), которое coq опять не может типизировать.

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

Поэтому в который раз, убедился, что из-за обилия в формальном доказательстве технических деталей тенденции к росту длин имён сущностей и тупо объёма кода, по которому не всегда просто навигироваться, любой нетривиальный факт надо сначала доказать более неформально на листочке, а уже потом переписывать формально. На фотке мои заметки к доказательству, что мой вариант разворачивается BFS'ом в интервал. Без плана в виде этих закорючек за деревьями леса уже было совсем не видно и я напрочь перестал понимать, что доказываю в данный момент, и что с этим надо делать.

notes

пятница, 12 августа 2011 г.

i3-wm

поюзал немного i3. довольно кавайный.
скриншоты и скринкаст с описанием основных возможностей прилагаются: http://i3wm.org/screenshots/

статус-бар и dmenu работают норм. трей прикручивается.

идея представления столов, как деревьев - прикольна. но по началу не интуитивно (например, не всегда очевидно, как добавить уже открытые окна к контейнеру) да и видимость страдает: хз какой у текущего контейнера layout или разбивка (для default layout). эта система пока несколько ограничена (об этом дальше), а к тому же глючит и падает (хоть и не так часто: за 2 дня экспериментов упало 1 раз).
однако стоит отдать должное, при падении предлагаются варианты: перезагрузиться с той же раскладкой окон, перезагрузиться с пустой раскладкой и выйти. первые 2 варианта сохраняют открытые приложения и их окна. (в моем случае первый не сработал, а второй скидал все окна в кучу на первый стол, но по крайней мере не убил).

full-screen приложения и режим работают отлично, а вот плавающие окна.. диалоги программ запускаются в плавающем режиме искаробки - это гуд. но в остальном - все плохо: переключаться между ними нельзя (только мышой), создавать из плавающего окна контейнер - нельзя и свернуть их тоже некуда.

не хватает всяких фишек.
нет возможности свернуть окно в полосу (скажем, в default layout, он же - стандартный тайлинг) с возможностью восстановления исходного размера (хотя бы горизонтальную, но и вертикальная бы не помешала). такое, вроде, было в ion'e, но он какой-то мертвый (даже сайт уже удален).
как уже говорил, хотелось бы какой-го нибудь дока для плавающих окошек.
и не помешала бы возможность циклического переключения между окнами.

P.S. мои вопросы:
  1. как-нибудь бы прикрутить пользовательское меню (таки удобная штука) + заюзать контент стандартного меню xfce, например (как туда софт добавляется, кстати?). можно такое из dmenu сделать?
  2. что еще можно попробовать с моими запросами?

суббота, 9 октября 2010 г.

run ebean from jar

Ebean по умолчанию не ищет классы entity'ей в jar'ах
исправляется указанием в его конфиге такого свойства:
ebean.search.jars=<разделенные запятыми имена jar'ок>

мою багу в ebean'е пофиксили))

суть: Ebean при использовании с mysql не правильно работает с полями типа boolean(выдает exception)
линк: www.avaje.org/bugdetail-323.html
причем довольно оперативно)

суббота, 1 мая 2010 г.

Числа Чёрча

Должен был рассказывать в универе про лямбда-исчисление.. набросал такой примерчик(все честно спизжено с wiki и переписано на схему):


; numbers
;; zero = λf.λx.x
(define zero
(lambda (f) (lambda (x) x)))
;; λf.λx.f x
(define one
(lambda (f) (lambda (x) (f x))))
;; λf.λx. f (f x)
(define two
(lambda (f) (lambda (x) (f (f x)))))
(define three (add-1 two))
(define four (add-1 three))

; operations
;; add-1 n = n+1 = λf.λy.f ((n f) y)
(define (add-1 n)
(lambda (f) (lambda (x) (f ((n f) x)))))
;; a+b
(define (add a b)
(lambda (f) (lambda (x) ((a f) ((b f) x)))))
;; mult = m*n = λm.λn.λf. n (m f)
(define (mult m n)
(lambda (f) (n (m f))))

; predicates
;; zero? ≡ λn. n (λx.F) T
(define zero?
(lambda (n) ((n (lambda (x) #f)) #t)))
;;; n-1 = λn.λf.λx. n (λg.λh. h (g f)) (λu. x) (λu. u)
(define (n-1 n)
(lambda (f) (lambda (x) (((n (lambda (g) (lambda (h) (h (g f)))))
(lambda (u) x))
(lambda (u) u)))))

; converting
;; get-number - convert Church numbers to Scheme numbers
(define (get-number n)
(if (zero? n)
0
(1+ (get-number (n-1 n)))))

; test
(zero? zero)
(zero? one)
(zero? (n-1 (add-1 zero)))
(get-number zero)
(get-number one)
(get-number three)
(get-number (add two three))
(get-number (mult one one))
(get-number (mult zero one))
(get-number (mult one three))
(define five (add two three))
(define six (mult two three))
(get-number (mult six five))

(zero? two)
(zero? (n-1 two))
(zero? (n-1 (n-1 two)))

; β-reduction example of (n-1 two)
;; (λn.λf.λx. n (λg.λh. h (g f)) (λu. x) (λu. u)) (λfx.f (f x))
;; λf.λx. (λfx.f (f x)) (λg.λh. h (g f)) (λu. x) (λu. u))
;; λf.λx. (λx.(λg.λh. h (g f)) ((λg.λh. h (g f)) x)) (λu. x) (λu. u))
;; λf.λx. ((λg.λh. h (g f)) ((λg.λh. h (g f)) (λu. x))) (λu. u))
;; λf.λx. (λh. h (((λg.λh. h (g f)) (λu. x)) f)) (λu. u)
;; λf.λx. (λu. u) (((λg.λh. h (g f)) (λu. x)) f))
;; λf.λx. (((λg.λh. h (g f)) (λu. x)) f)
;; λf.λx. ((λh. h ((λu. x) f)) f)
;; λf.λx. f x


P.S. решил что проще поменять цветовую схему блога под настройки в емаксе, чем наоборот) а уж воевать с htmlize - эт точно на долго бы затянулось..

пятница, 12 марта 2010 г.

thunderbird default browser

В моей (gentoo-linux) версии thunderbird'а не настраивается стандартным образом браузер, которым следует открывать ссылки. После обновления до thunderbird3 старый хак перестал работать.
Решение:
установить в Edit->Preferences->Advanced->Config Editor.
network.protocol-handler.warn-external.http=true
network.protocol-handler.warn-external.https=true
после чего при попытке открыть ссылку появится окно с запросом, в котором можно выбрать браузер или указать путь к нему

четверг, 11 февраля 2010 г.

Emacs: находки дня

  1. В емаксе можно держать несколько независимых буферов info. Ура! M-n - клонирует текущий инфо-буфер. C-n C-h i - открывает буфер *info*<n>
  2. можно сохранить текущую раскладку window'ов по fram'у и буферов по window'ам в регистр: C-x r w R, а потом соответственно ее загрузить оттуда: C-x r j R. Осталось только разобраться: "можно ли и как?" - сохранять это в bookmark'и
P.S. аа!! наглое дублирование лиспо-кода:
(defun bookmark-make-record-default (&optional point-only)
"Return the record describing the location of a new bookmark.
Must be at the correct position in the buffer in which the bookmark is
being set.
If POINT-ONLY is non-nil, then only return the subset of the
record that pertains to the location within the buffer."

`(,@(unless point-only `((filename . ,(bookmark-buffer-file-name))))
(front-context-string
. ,(if (>= (- (point-max) (point)) bookmark-search-size)
(buffer-substring-no-properties
(point)
(+ (point) bookmark-search-size))
nil))
(rear-context-string
. ,(if (>= (- (point) (point-min)) bookmark-search-size)
(buffer-substring-no-properties
(point)
(- (point) bookmark-search-size))
nil))
(position . ,(point))))

суббота, 12 сентября 2009 г.

регулярные выражения в NetBeans

При использовании Поиска/Замены в netbens'е, обнаружил странную особенность: при включенных регэкспах, использование строке для замены подстановок вида \n (которые для подстановки групп) не работают, т.е. ничего вместо них не подставляется.
Не знаю сколько бы я с этим возился, еслиб не догадался заглянуть в справку..
Оказалось, что синтаксис подстановок зачем-то изменен и надо использовать синтаксис: $n

понедельник, 7 сентября 2009 г.

о проприетарщине ;)

(gentoo)
emerge -uND world
<...>
* Messages for package www-plugins/adobe-flash-10.0.32.18:

* Flash player is closed-source, with a long history of security
* issues. Please consider only running flash applets you know to
* be safe. The 'flashblock' extension may help for mozilla users:
* https://addons.mozilla.org/en-US/firefox/addon/433
<...>

вот какой идиот придумал для потокового видео использовать Flash, да еще и обязательно самый свежий?!
Есь по кр. мере 3 OpenSource пакета, обеспечивающие поддержку Flash в FF(в т.ч.), но они обязательно несовметимы с последней версией Adobe Flash((

пятница, 31 июля 2009 г.

ЛКШ

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

вот такое, например:

Задание B-T2

Найдите ошибку в решении задачи и приведите верное решение.
Требуется подсчитать, сколько существует правильных скобочных последовательностей из N пар
круглых скобок.
Обозначим искомое количество через CN . Ясно, что C1 = 1.
Рассмотрим выражение из N пар скобок. Если первой слева открывающейся скобке соответствует
k-я слева закрывающаяся, и k < N , то выражение имеет вид: AB, где A — правильная скобочная
последовательность из k пар скобок, а B — правильная скобочная последовательность из N − k пар
скобок. Таких скобочных последовательностей — Ck CN −k , при этом k может принимать значение от 1
до N − 1.
Если же первой открывающейся скобке соответствует N -я закрывающаяся, то выражение имеет
вид (. . .), где вместо многоточия расположена правильная скобочная последовательность из N − 1
пары скобок. Количество таких последовательностей равно CN −1 .
Отсюда получаем формулу:
CN = (C1 CN −1 + C2 CN −2 + . . . + CN −1 C1 ) + CN −1


Я бы сходу такое, точно, не придумал.. +совершенно не понимаю, где здесь может быть ошибка((

lisp - это магия

текст заметки

я сначала ее написал, а потом уже подумал, что стоит отдельный блог под кодинг выделить.. вотЪ

суббота, 25 июля 2009 г.

sicp ex 2.13

немного по короче, чем везде, где я видел:
если интервалы заданы как
(make-center-percent c1 (* p1 100))
(make-center-percent c2 (* p2 100))

, то произведение
[c1(1-p1);c1(1+p1)]*[c2(1-p2);c2(1+p2)] =
[c1*c2*(1-(p1+p2)+p1*p2);c1*c2*(1+(p1+p2)+p1*p2)] ~=
[c1*c2*(1-(p1+p2));c1*c2*(1+(p1+p2))]

Т.о. p* ~= p1+p2, а c* = c1*c2.
Где результат равен
(make-center-percent c* (* p* 100))

sicp ex 1.19

sicp - Structure and Organisation of Computer Programms
буду писать здесь всякие замечания по поводу заданий из этой книги

(define (fib3 n)
(define (iter a b p q count)
(cond ((= count 0) b)
((even? count)
(iter a
b
(+ (square p) (square q))
(* q (+ (* 2 p) q))
(/ count 2)))
(else (iter (+ (* b q) (* a q) (* a p))
(+ (* b p) (* a q))
p
q
(- count 1)))))
(iter 1 0 0 1 n))

интересно, что время вычисления этим способом по сравнению с итеративным, хоть и меньше раз в 15, но растет с той же скоростью. Т.е. на самом деле логарифмическое время получить не удалось(оно скорее квадратичное)
мои результаты:
n  , итеративный процесс , "логарифмический", отношение
1e5, 1.559 , .080 , 19.5
1e6, 129.96 , 8.39 , 16.1
2e6, 508.92 , 35.23 , 14.5

для подсчетов использовал (runtime)

P.S. это на MIT Scheme