# увеличение размера истории
export HISTSIZE=2000
export HISTFILESIZE=2000
среда, 22 сентября 2010 г.
Увеличить размер history
Чтобы увеличить размер history в Ubuntu, достаточно добавить две строчки в файл ~/.bashrc:
вторник, 21 сентября 2010 г.
Печать из Linux в Windows
Задача: печатать из Linux на принтере Canon, к которому под Linux нет драйверов. Принтер подключён к компьютеру, который работает под Windows.
Решение.
1. На компьютере с Windows ставим четвёртый акробат. Файл называется acrobat405.exe, где я его нашёл, уже не помню.
2. Пишем нехитрый CGI-скрипт на newLISP, который будет по команде снаружи брать файл из указанной папки и печатать его. Скрипт я назвал printit.cgi
--- Начало printit.cgi ---
Как можно заметить, в скрипте указан путь к Акробату и путь к расшаренной папке, куда будут скидываться файлы из Linux. Наверное, излишне говорить, что для работы скрипта нужен установленный в папку c:\pdfpipe\NewLisp.exe.
3. Делаем батничек start.bat с единственной строчкой внутри:
start /b newlisp.exe newlisp -c -d 8003
4. Делаем батничек startup.vbs, который будет запускать start.bat тихо, не нервируя пользователя. Внутри батничка уже две строчки:
Set oShell = WScript.CreateObject("WScript.Shell")
oShell.Run "server.bat", 0, False
5. Создаём папку для документов и расшариваем её.
6. Дальше работаем на компьютере с Linux. Подразумевается, что cups-pdf и lynx у нас установлены.
7. Открываем файл /etc/cups/cups-pdf.conf. Сообщаем, что печатать теперь надо в другую папку: вместо «Out ${HOME}/PDF» пишем «Out /var/spool/pdfprint». Папку /var/spool/pdfprint надо будет создать и проставить на неё разрешения.
8. Ближе к концу cups-pdf.conf вставляем вместо закомментированного PostProcessing строчку «PostProcessing /var/spool/pdfprint/script.lsp»
9. А вот и сам файлик script.lsp:
--- Начало script.lsp ---
#!/usr/bin/newlisp
(set 'pdf-path "/var/spool/pdfprint/")
(dolist (x (directory pdf-path "\\.pdf"))
(exec (append "mv " pdf-path x " " pdf-path "pool"))
(exec (append "lynx 192.168.25.101:8003/printit.cgi?file=" x " -dump")))
(exit)
--- Конец script.lsp ---
Где править айпишник видно. Это айпишник компьютера с Windows к которому подключён принтер.
10. Тонкий момент. С помощью apparmor надо разрешить cups-pdf печатать в выбранную нами папку. Как именно это сделать я уже забыл, но в Интернетах про это много.
11. Наконец, надо подсоединить сетевую папку. В /etc/fstab пишем что-то типа:
//192.168.25.101/canon /var/spool/pdfprint/pool cifs rw,user=print,pass=print,iocharset=utf8,file_mode=0777,file_mode=0777,dir_mode=0777
Зачем два раза писать file_mode не спрашивайте. У меня стоит так и работает. Наверное, будет работать и с одним file_mode, но мне было лень проверять.
12. Вот, в общем, и всё. На всякий случай, как всё работает.
а) Печатаем в PDF
б) файл blablabla.pdf кладётся в /var/spool/pdfprint
в) исполняется скрипт /var/spool/pdfprint/script.lsp
г) Скрипт кладёт файл blablabla.pdf в сетевую папку /var/spool/pdfprint/pool
д) Скрипт через lynx даёт команду cgi-скрипту на компьютере windows печатать файл blablabla.pdf
е) Скрипт printit.cgi запускает акробат, чтобы тот напечатал blablabla.pdf и стирает файл.
13. Инструкцию пишу по памяти. Прямо сейчас у меня всё работает, но, вполне вероятно, про какую-то мелочь я забыл упомянуть.
Решение.
1. На компьютере с Windows ставим четвёртый акробат. Файл называется acrobat405.exe, где я его нашёл, уже не помню.
2. Пишем нехитрый CGI-скрипт на newLISP, который будет по команде снаружи брать файл из указанной папки и печатать его. Скрипт я назвал printit.cgi
--- Начало printit.cgi ---
#!c:\pdfpipe\NewLisp.exe
(module "cgi.lsp")
(set 'pool-path "C:\\pdfpipe\\pdfpool\\") ;
(set 'acrobat-path "\"c:\\Program Files\\Adobe\\Acrobat 4.0\\Reader\\AcroRd32.exe\"")
(set 'printer-name "OneNote")
(set 'file-to-print (CGI:get "file"))
(exec (append acrobat-path " /t /n " pool-path file-to-print " " printer-name))
(exec (append "del " pool-path file-to-print))
--- Конец printit.cgi ---
Как можно заметить, в скрипте указан путь к Акробату и путь к расшаренной папке, куда будут скидываться файлы из Linux. Наверное, излишне говорить, что для работы скрипта нужен установленный в папку c:\pdfpipe\NewLisp.exe.
3. Делаем батничек start.bat с единственной строчкой внутри:
start /b newlisp.exe newlisp -c -d 8003
4. Делаем батничек startup.vbs, который будет запускать start.bat тихо, не нервируя пользователя. Внутри батничка уже две строчки:
Set oShell = WScript.CreateObject("WScript.Shell")
oShell.Run "server.bat", 0, False
5. Создаём папку для документов и расшариваем её.
6. Дальше работаем на компьютере с Linux. Подразумевается, что cups-pdf и lynx у нас установлены.
7. Открываем файл /etc/cups/cups-pdf.conf. Сообщаем, что печатать теперь надо в другую папку: вместо «Out ${HOME}/PDF» пишем «Out /var/spool/pdfprint». Папку /var/spool/pdfprint надо будет создать и проставить на неё разрешения.
8. Ближе к концу cups-pdf.conf вставляем вместо закомментированного PostProcessing строчку «PostProcessing /var/spool/pdfprint/script.lsp»
9. А вот и сам файлик script.lsp:
--- Начало script.lsp ---
#!/usr/bin/newlisp
(set 'pdf-path "/var/spool/pdfprint/")
(dolist (x (directory pdf-path "\\.pdf"))
(exec (append "mv " pdf-path x " " pdf-path "pool"))
(exec (append "lynx 192.168.25.101:8003/printit.cgi?file=" x " -dump")))
(exit)
--- Конец script.lsp ---
Где править айпишник видно. Это айпишник компьютера с Windows к которому подключён принтер.
10. Тонкий момент. С помощью apparmor надо разрешить cups-pdf печатать в выбранную нами папку. Как именно это сделать я уже забыл, но в Интернетах про это много.
11. Наконец, надо подсоединить сетевую папку. В /etc/fstab пишем что-то типа:
//192.168.25.101/canon /var/spool/pdfprint/pool cifs rw,user=print,pass=print,iocharset=utf8,file_mode=0777,file_mode=0777,dir_mode=0777
Зачем два раза писать file_mode не спрашивайте. У меня стоит так и работает. Наверное, будет работать и с одним file_mode, но мне было лень проверять.
12. Вот, в общем, и всё. На всякий случай, как всё работает.
а) Печатаем в PDF
б) файл blablabla.pdf кладётся в /var/spool/pdfprint
в) исполняется скрипт /var/spool/pdfprint/script.lsp
г) Скрипт кладёт файл blablabla.pdf в сетевую папку /var/spool/pdfprint/pool
д) Скрипт через lynx даёт команду cgi-скрипту на компьютере windows печатать файл blablabla.pdf
е) Скрипт printit.cgi запускает акробат, чтобы тот напечатал blablabla.pdf и стирает файл.
13. Инструкцию пишу по памяти. Прямо сейчас у меня всё работает, но, вполне вероятно, про какую-то мелочь я забыл упомянуть.
пятница, 6 августа 2010 г.
CentOS, crypto.lsp
Модуль crypto.lsp не заработал сразу. Так как для него нужна библиотека libcrypto.so, которая в моём CentOS по умолчанию установлена не была. Проблема решилась в две команды.
Команда первая — выдать список пакетов с этой библиотекой:
Команда вторая — установить один из пакетов, содержащих нужную библиотеку:
Вуаля.
Команда первая — выдать список пакетов с этой библиотекой:
yum provides "*/libcrypto.so*"
Команда вторая — установить один из пакетов, содержащих нужную библиотеку:
sudo yum install openssl-devel-0.9.8e-12.el5_4.6.i386
Вуаля.
среда, 21 июля 2010 г.
Скрипты из Windows не запускаются под Linux
Скрипты из Windows не запускались, выдавая ошибку (2)No such file or directory: exec of '.../index.cgi' failed.
Двухчасовое расследование показало, что проблема в DOS-формате скриптов: конец строки обозначался двумя символами, а не одним, поэтому Апач не мог правильно обработать первую строчку.
Установленный vim, однако, не показывал привычного ^M, поэтому я понял это только когда начал сравнивать размеры файлов.
Команда "dos2unix index.cgi" решила проблему.
Двухчасовое расследование показало, что проблема в DOS-формате скриптов: конец строки обозначался двумя символами, а не одним, поэтому Апач не мог правильно обработать первую строчку.
Установленный vim, однако, не показывал привычного ^M, поэтому я понял это только когда начал сравнивать размеры файлов.
Команда "dos2unix index.cgi" решила проблему.
Установка newLISP на CentOS
Как установить newLISP на CentOS автоматически я не нашёл. Зато замечательно сработала установка через компиляцию исходников:
wget http://www.newlisp.org/downloads/newlisp-10.2.8.tgz
tar -xf newlisp-10.2.8.tgz
cd newlisp-10.2.8
sudo ./configure-alt
sudo make
sudo make install
четверг, 8 июля 2010 г.
Ubuntu: переименовать файлы по маске
Мне потребовалось переименовать файлы по маске: заменить файлы вида 1003.123 на 1003.10123. Получилось вот такой командой:
При этом maxdepth 1 означает "не спускаться ниже по дереву", а команда rename имеет шаблон "/s/from/to/"
find . -maxdepth 1 -iname "1003.*" -execdir rename "s/1003\./1003\.10/" \{\} \;
При этом maxdepth 1 означает "не спускаться ниже по дереву", а команда rename имеет шаблон "/s/from/to/"
вторник, 29 июня 2010 г.
newLISP: curry-all
Обнаружил, что после перехода на новую версию newLISP перестал работать мой curry-all (который должен делать то же самое, что и curry, только с несколькими аргументами). Вот переставший работать вариант:
А вот вариант работающий, позаимствованный у Лутца:
Кстати, Лутц предлагает ещё одно решение, вот здесь:
http://newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=3169
(context 'curry-all)
(define-macro (curry-all:curry-all f)
(letex (f1 f lst (map string (args)))
(fn (z) (eval-string (append "(" (name 'f1) " " (join 'lst " ") " curry-all:z)")))))
(context 'MAIN)
А вот вариант работающий, позаимствованный у Лутца:
(define (curry-all f)
(append (lambda (z)) (list (cons f (append (args) '(z))))))
Кстати, Лутц предлагает ещё одно решение, вот здесь:
http://newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=3169
newLISP 10.2.8 и Ubuntu 8.10
Установил новую версию newLISP: 10.2.8. Пишет, что ему нужна библиотека libreadline6, а у меня только libreadline5.
Обновил свою 8.10 до 9.04. Не помогло.
Заглянул на newlisp.org. Там Lutz пишет, что можно настроить автоматическую установку newLISP через sudo apt-get install newlisp, но... только в 9.10 и выше.
Мне кажется, это намёк. Сейчас обновлюсь до 9.10 и, очень надеюсь, всё заработает.
Обновил свою 8.10 до 9.04. Не помогло.
Заглянул на newlisp.org. Там Lutz пишет, что можно настроить автоматическую установку newLISP через sudo apt-get install newlisp, но... только в 9.10 и выше.
Мне кажется, это намёк. Сейчас обновлюсь до 9.10 и, очень надеюсь, всё заработает.
среда, 21 апреля 2010 г.
Lock: продолжение.
Придумал новый алгоритм для лоченья файлов. Где можно наколоться пока не вижу. Но опыт, конечно, покажет. Также у меня всегда остётся запасной вариант: man 1 flock, man 2 flock.
Допустим, нам надо изменить test.txt
1. Проверяем, нет ли test.txt.lock, изменённого менее 30 секунд назад.
2. Если есть, ждём 100 ms, идём на пункт 1.
3. Стираем лок-файл, изменённый старше 30 секунд:
find . -maxdepth 1 -mmin +0,5 -name test.txt.lock -delete
(Команду надо ещё проверить). Стирать хочется именно одной командой, чтобы во время её выполнения другой скрипт не вставил свой файл.
4. Делаем аппенд к лок-файлу:
(append-file "test.txt.lock" (append id "\n"))
5. Если мы не успели, идём на пункт 1.
6. Мы успели. Делаем необходимые манипуляции с test.txt.
7. Стираем lock-файл.
Допустим, нам надо изменить test.txt
1. Проверяем, нет ли test.txt.lock, изменённого менее 30 секунд назад.
2. Если есть, ждём 100 ms, идём на пункт 1.
3. Стираем лок-файл, изменённый старше 30 секунд:
find . -maxdepth 1 -mmin +0,5 -name test.txt.lock -delete
(Команду надо ещё проверить). Стирать хочется именно одной командой, чтобы во время её выполнения другой скрипт не вставил свой файл.
4. Делаем аппенд к лок-файлу:
(append-file "test.txt.lock" (append id "\n"))
5. Если мы не успели, идём на пункт 1.
6. Мы успели. Делаем необходимые манипуляции с test.txt.
7. Стираем lock-файл.
вторник, 20 апреля 2010 г.
newLISP: самодельная блокировка текстовых файлов
Проблема. Скрипт создаёт по запросу пользователей текстовые заявки с возрастающими номерами. Если два пользователя одновременно нажмут "создать заявку", может создаться две заявки с одинаковым номером. Тогда у одного из пользователей заявка не сохранится (будет затёрта вторым).
Решение.
1. Делаем в папке с заявками файл lock.
2. Убеждаемся, что генератор случайных чисел сеется. Я сею его на основе выбранного порта и системного времени. Подозреваю, в windows может не работать, если там time-of-day возвращается с точностью до секунды, а не до миллисекунды.
3. Вот собственно процедура получения номера. Суть простая. Пишем через append в файл "lock" номер, который мы хотим занять и подписываем его случайным числом. Проверяем, что никто не успел вклиниться перед нами. Если мы опоздали (номер уже занят), повторяем попытку.
Я тестировал на одновременных 100 подключениях. Работало с двойным запасом.
4. Вот воспомогательные процедуры:
5. Замечания.
5.1. Лог может сильно разрастись, надо как-то его стирать изредка.
5.2. Если мы стёрли файл, а в lock он остался, в нумерации будут пропуски. Это скорее фича, но тем не менее.
5.3. Процедура блокировки файлов для изменения не проработана. Если мы хотим обезопаситься ещё и от изменения, нужно думать дальше.
5.4. Схема "велосипедна". То есть, лучше бы, вообще, пользоваться базами данных.
Решение.
1. Делаем в папке с заявками файл lock.
2. Убеждаемся, что генератор случайных чисел сеется. Я сею его на основе выбранного порта и системного времени. Подозреваю, в windows может не работать, если там time-of-day возвращается с точностью до секунды, а не до миллисекунды.
(seed (div (time-of-day) (int (env "REMOTE_PORT")) 0.000001))
3. Вот собственно процедура получения номера. Суть простая. Пишем через append в файл "lock" номер, который мы хотим занять и подписываем его случайным числом. Проверяем, что никто не успел вклиниться перед нами. Если мы опоздали (номер уже занят), повторяем попытку.
(define (occupy-number) (local (id num res)
(set 'id (rand-string))
(set 'num (last (or (sort (map int (directory "." "txt$"))) '(0))))
(do-until (= (or (find (s-a num ":" id) res 0) "no") (find (s-a num ":.*") res 0))
(inc num)
(append-file "lock" (s-a num ":" id "\n"))
(set 'res (read-tail "lock" 100)))
num))
Я тестировал на одновременных 100 подключениях. Работало с двойным запасом.
4. Вот воспомогательные процедуры:
(define (string-append) (apply append (map string (args))))
(set 's-a string-append)
; В Windows реальный rand (насколько я помню) имеет диапазон в 32768,
; поэтому я его для надёжности "утроил".
(define (rand-string)
(join (map string (list (rand 65536) (rand 65536) (rand 65536))) "_"))
; Возвращает последние 'lines строк файла в виде списка.
; Работает, по понятным причинам, только под *nix
(define (read-tail fname (lines 10))
(exec (append "tail " fname " -n " (string lines))))
5. Замечания.
5.1. Лог может сильно разрастись, надо как-то его стирать изредка.
5.2. Если мы стёрли файл, а в lock он остался, в нумерации будут пропуски. Это скорее фича, но тем не менее.
5.3. Процедура блокировки файлов для изменения не проработана. Если мы хотим обезопаситься ещё и от изменения, нужно думать дальше.
5.4. Схема "велосипедна". То есть, лучше бы, вообще, пользоваться базами данных.
JavaScript: закрывать псевдопопап по Escape
Как закрывать псевдопопапы по нажатию Esc.
1. Ставим обработчик в body:
2. Создаём массив, в котором будет храниться список открытых окон:
3. Запиливаем вот эти функции:
Про вычисление координат для окна я уже писал раньше:
http://hilocomod.blogspot.com/2010/03/js.html
1. Ставим обработчик в body:
<body onkeydown="if (event.keyCode == 27) closePopup();">
2. Создаём массив, в котором будет храниться список открытых окон:
// Список открытых окон
var popupList = new Array();
3. Запиливаем вот эти функции:
// Закрывает открытое окно
function closePopup() {
if (popupList.length == 0) return;
document.getElementById(popupList.pop()).style.display = 'none';
}
// Удаляет элемент из массива
function popAssoc(arr,itm) {
var arr1 = new Array();
for (i=0;i<arr.length;i++) {
if (arr[i] != itm) arr1.push(arr[i]);
}
return arr1;
}
// Показывает всплывающее окно по центру экрана рядом с курсором мыши
function showWin(id, event, firstField) {
// Если окно уже показано, прячем его
if (document.getElementById(id).style.display == 'block') {
popupList = popAssoc(popupList,id);
document.getElementById(id).style.display = 'none';
} else {
// Запоминаем окно в списке открытых
popupList.push(id);
// Вычисляем координаты
thisWin = document.getElementById(id);
thisWin.style.top = getYCoord(parseInt(thisWin.style.height), event);
thisWin.style.left = (document.body.clientWidth - parseInt(thisWin.style.width)) / 2;
// Показываем окно
thisWin.style.display = 'block';
// Фокусируемся на первом поле
if (firstField) setFocus(firstField);
}
}
Про вычисление координат для окна я уже писал раньше:
http://hilocomod.blogspot.com/2010/03/js.html
суббота, 17 апреля 2010 г.
SICP: малая теорема Ферма
Кусочек кода с распознаванием простых чисел по Малой Теореме Ферма.
Функция carmaicl? работает довольно медленно. Чтобы прогнать её на числах от 2 до 5 000 потребовалось несколько минут. При этом на проверку 2х функция затрачивает примерно в 2 раза больше времени, чем на проверку х. То есть, если я правильно понимаю, на проверку диапазона 5k-10k уйдёт в три раза больше времени, чем на проверку диапазона 0k-5k.
; Разное встроенное
(define (even? x) (= (% x 2)))
(define (square x) (* x x))
(define (prime? p) (= (first (factor p)) p))
; Вычисляет (% (pow a b) p)
(define (expmod a b p)
(cond ((= b) 1)
((even? b) (% (square (expmod a (/ b 2) p)) p))
(true (% (* a (expmod a (dec b) p)) p))))
; Проверяет число на простоту 1 раз
(define (fermat-test p)
(let (n (+ (rand (- p 1)) 1))
(= (expmod n p p) n)))
; Проверяет число на простоту много раз
(define (fast-prime p (n 1))
(local (f)
(while (and (set 'f (fermat-test p)) (!= (dec n))))
f))
; Проверяет число на принадлежность к Кармайкловым Числам.
(define (carmaicl? x)
(and (not (prime? x))
(apply and (map (fn (n) (= (expmod n x x) n)) (sequence 1 (- x 1))))))
Функция carmaicl? работает довольно медленно. Чтобы прогнать её на числах от 2 до 5 000 потребовалось несколько минут. При этом на проверку 2х функция затрачивает примерно в 2 раза больше времени, чем на проверку х. То есть, если я правильно понимаю, на проверку диапазона 5k-10k уйдёт в три раза больше времени, чем на проверку диапазона 0k-5k.
Uptime в среде Windows
Как известно, uptime в среде Windows можно получить, набрав "net stats server".
Чтобы перевести выдаваемый текст в секунды аптайма, пришлось написать вот такую функцию:
Комментарии.
1. parse-date не работает в Win32.
2. (date-value параметры) возвращает время GMT. В моём случае разница составила 4 часа. Чтобы скорректировать пришлось отнять часы в (date) от часов в (now).
3. Время выполнения функции — примерно 125 миллисекунд.
Чтобы перевести выдаваемый текст в секунды аптайма, пришлось написать вот такую функцию:
; Показывает Uptime в среде Windows
(define (uptime)
(let (wt (regex {(\d+)/(\d+)/(\d{4}) (\d+):(\d+) ([AP]M)} (join (exec "net stats server"))))
(if (= (wt 18) "PM") (setf (wt 12) (+ (int (wt 12)) 12)))
(- (date-value)
(* (% (+ 24 (int ((regex {\d+(?=:)} (date)) 0)) (- ((now) 3))) 24) -3600)
(apply date-value (append (select (map int wt) 9 3 6 12 15) '(0))))))
Комментарии.
1. parse-date не работает в Win32.
2. (date-value параметры) возвращает время GMT. В моём случае разница составила 4 часа. Чтобы скорректировать пришлось отнять часы в (date) от часов в (now).
3. Время выполнения функции — примерно 125 миллисекунд.
пятница, 16 апреля 2010 г.
SICP 1.19: Фибоначчи по логарифму
Вот эта гнусная функция:
Программирования, тут, на мой взгляд, гораздо меньше, чем математики. Думаю, это неправильно.
(define (even? x) (if (= (% x 2))))
(define (fib n (a 1) (b 0) (p 0) (q 1))
(cond ((= n) b)
((even? n) (fib (/ n 2)
a
b
(+ (* p p) (* q q))
(+ (* 2 p q) (* q q))))
(true (fib (dec n)
(+ (* b q) (* a q) (* a p))
(+ (* b p) (* a q))
p
q))))
Программирования, тут, на мой взгляд, гораздо меньше, чем математики. Думаю, это неправильно.
Чётные числа
Проверил, как быстрее считать чётность.
Как я и ожидал, вариант "в лоб" оказался примерно на порядок быстрее, да.
; Вариант 1, хитропопый
(define (even-1 x)
(if (find (last (string x)) '("0" "2" "4" "6" "8")) true))
; Вариант 2, "в лоб"
(define (even-2 x)
(if (= (% x 2))))
Как я и ожидал, вариант "в лоб" оказался примерно на порядок быстрее, да.
вторник, 13 апреля 2010 г.
SICP: итеративное возведение в степень
Долго думал, как сделать итеративный процесс возведения в степень (SICP, упражнение 1.16). Я сразу догадался, что брать за основу надо число в двоичной системе счисления, но мне было непонятно, как переводить это число в алгоритм.
Вот например, как выглядели алгоритмы и двоичная запись для некоторых чисел:
7 *+*+ 111
8 *** 1000
9 ***+ 1001
10 **+* 1010
В итоге оказалось всё просто. Надо отбрасывать самый первый знак, а дальше трактовать единицу как "возвести в квадрат и умножить", а ноль — как "просто возвести в квадрат".
Также я впервые использовал здесь функцию cons, которая объединяет first и rest.
А вот то же самое, только по "итеративному алгоритму" предложенному в SICP:
Вроде бы как, ошибиться тут просто негде. Однако… цитирую SICP:
«Храните, помимо значения степени n и основания b, дополнительную переменную состояния a, и определите переход между состояниями так, чтобы произведение abn от шага к шагу не менялось».
Спрашивается, зачем козе баян? Разве что, перехерачить алгоритм вот так:
Согласно тестам, кстати, самый быстрый алгоритм — это рекурсия без инварианта. Алгоритмы с инвариантом и с разложением на биты немного медленнее. Впрочем, скорости там одного порядка, существенной разницы нет.
Дополнение.
Блджад. Только перейдя к упражнению 1.17 я понял, в чём была соль. По условиям задачи, у нас есть вот такие функции:
Из них надо склепать умножение. Вот труе итеративный вариант:
А вот то же самое, но черезжопу концевую рекурсию:
Вот например, как выглядели алгоритмы и двоичная запись для некоторых чисел:
7 *+*+ 111
8 *** 1000
9 ***+ 1001
10 **+* 1010
В итоге оказалось всё просто. Надо отбрасывать самый первый знак, а дальше трактовать единицу как "возвести в квадрат и умножить", а ноль — как "просто возвести в квадрат".
Также я впервые использовал здесь функцию cons, которая объединяет first и rest.
; Возвести число b в степень p (p должно быть больше 1)
(define (pow-hm b p)
(apply (fn (x y) (if (= y) (* x x) (* x x b)))
(cons b (map int (1 (explode (bits p)))))
2))
А вот то же самое, только по "итеративному алгоритму" предложенному в SICP:
; Возвести b в степень n
(define (pow-sicp-1 b n)
(cond ((= n 1) b)
((= (% n 2)) (pow-sicp-1 (* b b) (/ n 2)))
(true (* (pow-sicp-1 (* b b) (/ n 2)) b))))
Вроде бы как, ошибиться тут просто негде. Однако… цитирую SICP:
«Храните, помимо значения степени n и основания b, дополнительную переменную состояния a, и определите переход между состояниями так, чтобы произведение abn от шага к шагу не менялось».
Спрашивается, зачем козе баян? Разве что, перехерачить алгоритм вот так:
; То же самое, но с инвариантом
(define (pow-sicp-2 b n (a 1))
(if (= n 1)
(* b a)
(* a (pow-sicp-2 (* b b) (/ n 2) (if (= (% n 2)) 1 b)))))
Согласно тестам, кстати, самый быстрый алгоритм — это рекурсия без инварианта. Алгоритмы с инвариантом и с разложением на биты немного медленнее. Впрочем, скорости там одного порядка, существенной разницы нет.
Дополнение.
Блджад. Только перейдя к упражнению 1.17 я понял, в чём была соль. По условиям задачи, у нас есть вот такие функции:
(define (double x) (* x 2))
(define (halve x) (/ x 2))
(define (even? x) (if (= (% x 2))))
Из них надо склепать умножение. Вот труе итеративный вариант:
; Умножить а на b
(define (mul-sicp-i a b (p 0))
(let (p 0)
(until (= b 1)
(set 'p (+ p (if (even? b) 0 a)))
(set 'a (double a))
(set 'b (halve b)))
(+ a p)))
А вот то же самое, но через
; Умножить a на b
(define (mul-sicp-t a b (p 0))
(if (= b 1)
(+ a p)
(mul-sicp-t (double a) (halve b) (+ p (if (even? b) 0 a)))))
воскресенье, 11 апреля 2010 г.
Способы разменять монеты
Хитроумный алгоритм по размену монет из первой главы SICP. Мне потребовалось значительное время, чтобы понять как он работает и воспроизвести его:
Это воссозданный мной алгоритм из книжки. Вначале же я сделал иначе: считал все возможные методы размена, потом через unique отбирал уникальные, потом через memoize добивался терпимой скорости. Тоже работало, плюс кроме числа способов размена я видел в списке ещё и сами способы.
Но решение из книжки, конечно, красивее.
Кстати, в самой книжке алгоритм реализован, на первый взгляд, примерно так же, но там есть слова: «на то, чтобы получить ответ 292 уйдёт какое-то время». В реальности время выполнения процедуры на моём компьютере — 0,3 миллисекунды.
То ли эта фраза писалась в семидесятые года, то ли newLISP гораздо круче чем Scheme.
PS: Разменять доллар можно 292 способами. Способов разменять 10 долларов уже 800 тысяч.
PPS: Те же яйца, но с печатью дерева.
#
# Число способов разменять n центов монетами по 1, 5, 10, 25 и 50 центов.
#
; Доступные монеты
(set 'монеты '(1 5 10 25 50))
; Число разменов n центов с помощью c монет.
(define (размены n (c 5))
(cond ((< n) 0)
((= n) 1)
((= c 1) 1)
(true (+ (размены n (- c 1))
(размены (- n (монеты (- c 1))) c)))))
Это воссозданный мной алгоритм из книжки. Вначале же я сделал иначе: считал все возможные методы размена, потом через unique отбирал уникальные, потом через memoize добивался терпимой скорости. Тоже работало, плюс кроме числа способов размена я видел в списке ещё и сами способы.
Но решение из книжки, конечно, красивее.
Кстати, в самой книжке алгоритм реализован, на первый взгляд, примерно так же, но там есть слова: «на то, чтобы получить ответ 292 уйдёт какое-то время». В реальности время выполнения процедуры на моём компьютере — 0,3 миллисекунды.
То ли эта фраза писалась в семидесятые года, то ли newLISP гораздо круче чем Scheme.
PS: Разменять доллар можно 292 способами. Способов разменять 10 долларов уже 800 тысяч.
PPS: Те же яйца, но с печатью дерева.
(define (размены n (c 5))
(print "Р" n " (" c "): ")
(cond ((< n) (println 0) 0)
((= n) (println 1) 1)
((= c 1) (println 1) 1)
(true (println "Р" n " (" (- c 1) ") + Р" (- n (монеты (- c 1))) " (" c ")")
(+ (размены n (- c 1)) (размены (- n (монеты (- c 1))) c)))))
пятница, 9 апреля 2010 г.
Хвостовая рекурсия
Немного доброты по ссылке с форума newLISP (Ссылка)
Подводя итог, эта точка зрения на хвостовую рекурсию основана на следующих идеях:
* Она предназначена для избегания циклов, которые по какой-то причине полагаются пуристами "плохими". Эти извращенцы думают в циклах, потом переводят свой код в хвостовую рекурсию, а потом ожидают от компилятора обратного перевода в циклы. Это чисто академическая ментальная гимнастика безо всякой практической цели.
* Люди, которым нравится хвостовая рекурсия, целенаправленно обмеменяют свой когд во имя "математической" или "теоретической" чистоты, ожидая при этом, что имлементатор языка сделает за них всю грязную работу.
* Любой код, который зависит от хвостовой рекурсии, легко может быть переписан в циклах. Такая перепись является тривиальной местной трансформацией.
* Межпроцедурная хвостовая рекурсия может привести к некоторому увеличению производительности, но это увеличение будет крайне мало, и за это увеличение придётся заплатить потерей ценной отладочной информации.
Подводя итог, эта точка зрения на хвостовую рекурсию основана на следующих идеях:
* Она предназначена для избегания циклов, которые по какой-то причине полагаются пуристами "плохими". Эти извращенцы думают в циклах, потом переводят свой код в хвостовую рекурсию, а потом ожидают от компилятора обратного перевода в циклы. Это чисто академическая ментальная гимнастика безо всякой практической цели.
* Люди, которым нравится хвостовая рекурсия, целенаправленно обмеменяют свой когд во имя "математической" или "теоретической" чистоты, ожидая при этом, что имлементатор языка сделает за них всю грязную работу.
* Любой код, который зависит от хвостовой рекурсии, легко может быть переписан в циклах. Такая перепись является тривиальной местной трансформацией.
* Межпроцедурная хвостовая рекурсия может привести к некоторому увеличению производительности, но это увеличение будет крайне мало, и за это увеличение придётся заплатить потерей ценной отладочной информации.
Фриддл, ДКА и НКА
Закончил читать книгу Фриддла про регулярные выражения.
Фриддл, конечно, молодец, сказать нечего. Но про автоматы и про разницу между ними он написал крайне непонятно. Гораздо понятнее вот здесь:
http://www.rsdn.ru/article/alg/nka.xml
Если бы я сначала потратил 10 минут на чтение этого текста, а только потом принялся бы за Фриддла, полагаю, я бы вынес из книги Фриддла гораздо больше пользы.
Фриддл, конечно, молодец, сказать нечего. Но про автоматы и про разницу между ними он написал крайне непонятно. Гораздо понятнее вот здесь:
http://www.rsdn.ru/article/alg/nka.xml
Если бы я сначала потратил 10 минут на чтение этого текста, а только потом принялся бы за Фриддла, полагаю, я бы вынес из книги Фриддла гораздо больше пользы.
четверг, 8 апреля 2010 г.
SICP
Начал читать волшебную книгу: SICP
Вот моя программка получения квадратного корня:
Забавно, что программа находит, например, корень из двух всего за шесть итераций. Я раньше думал, будто нахождение корня — сложный и медленный алгоритм.
Алсо, в newLISP есть функция constant. Если хочется переопределить часть символов, чтобы использовать + вместо add и - вместо div, надо использовать её.
А вот функция для куба.
Что характерно, на этот раз функция more-exact сделана локальной и, следовательно, не только не будет занимать ценное имя, но и сможет использовать родительский "х".
Вот моя программка получения квадратного корня:
(define (more-exact x pre)
(let (next-pre (div (add (div x pre) pre) 2))
(if (= pre next-pre) pre (more-exact x next-pre))))
(define (squa x) (more-exact x 1))
Забавно, что программа находит, например, корень из двух всего за шесть итераций. Я раньше думал, будто нахождение корня — сложный и медленный алгоритм.
Алсо, в newLISP есть функция constant. Если хочется переопределить часть символов, чтобы использовать + вместо add и - вместо div, надо использовать её.
А вот функция для куба.
(define (cube x)
(let (more-exact (fn (pre)
(let (next-pre (div (add (div x pre pre) pre pre) 3))
(if (= pre next-pre) pre (more-exact next-pre)))))
(more-exact 1)))
Что характерно, на этот раз функция more-exact сделана локальной и, следовательно, не только не будет занимать ценное имя, но и сможет использовать родительский "х".
понедельник, 5 апреля 2010 г.
JavaScript: навигация стрелками и Enter
Убил восемь (!) часов на скорбный труд по созданию этого монстрика.
/**
*
* Keyboard Form Navigation
* Использование: в конце документа исполните KFN.init ('form1');
*
**/
// Фокусируется на элементе
function setFocus(id) {
document.getElementById(id).focus();
setTimeout("document.getElementById('"+id+"').select();", 100);
}
// Возвращает случайный ID, не присутствующий ещё в документе
function randomId (){
do {
var newId = "rnd" + Math.floor((Math.random() * 1000000));
}
while (document.getElementById(newId) != null);
return newId;
}
// Набор функций, обеспечивающий навигацию по стрелкам и Enter
var KFN = {
// Список полей формы, по которым надо перемещаться стрелками
_elements : [],
// Форма субмиттится только если _flag == true
_allow_submit : false,
// Субмитит форму. Нужен, чтобы форма не субмитилась по каждому энтеру
_submit : function () {
if (this._allow_submit)
document.getElementById(this._elements[0]).form.submit();
},
// Инициализирует форму для работы со стрелками и Enter
// Меняет onFocus у всех полей формы.
init : function (formId) {
var thisForm = document.getElementById(formId);
thisForm.setAttribute("autocomplete", "off")
if (thisForm.addEventListener) {
thisForm.addEventListener("submit", function(e) { e.preventDefault(); }, false);
thisForm.addEventListener("keydown", function(e) { KFN.next(e); }, false);
}
else if (thisForm.attachEvent) {
// Ослик вместо preventDefault использует event.returnValue = false;
thisForm.attachEvent("onsubmit", function(e) { e.returnValue = false; });
thisForm.attachEvent("onkeydown", function(e) { KFN.next(e); });
}
for (var i = 0 , e = thisForm.elements, len = e.length; i < len; i++ ) {
if (/text|password|file|checkbox|radio|select/.test (e[i].type)) {
// Если у поля нет id, ставим какой-нибудь
if (!e[i].id) e[i].id = randomId();
this._elements.push (e[i].id);
// Автокомплект должен быть отключён как на форме, так и на всех полях.
e[i].setAttribute("autocomplete", "off")
// Без этих самодельных обработок мы бы не узнали, на каком элементе фокус
e[i].hasFocus=false;
e[i].onfocus=function(){this.hasFocus=true;};
e[i].onblur =function(){this.hasFocus=false;};
} else if (e[i].type == "submit") {
// Обработка нажатия мышью на обычную субмит-кнопку
e[i].onfocus=function(){KFN._allow_submit=true;}
e[i].onblur=function(){KFN._allow_submit=false;}
if (e[i].addEventListener) {
e[i].addEventListener("click", function() { KFN._submit(); }, false);
} else if (e[i].attachEvent) {
e[i].attachEvent("onclick", function() { KFN._submit(); });
}
}
}
// Фокусируемся на первом поле формы
setFocus (this._elements[0]);
},
// Переходит к следующему полю формы
next: function (input) {
// Обрабатываем только три клавиши. 13: Enter, 38: Up, 40: Down
if ((input.keyCode != 13) && (input.keyCode != 38) && (input.keyCode != 40))
return true;
// Находим текущий элемент (по фокусу)
for (var i=0; i<this._elements.length; i++)
if (document.getElementById(this._elements[i]).hasFocus)
var current = i;
if (current === undefined) return true;
// Перемещаем фокус
switch (input.keyCode) {
case 13: // Enter
if ((current + 1) < this._elements.length) {
setFocus (this._elements[1+current]);
} else {
document.getElementById(this._elements[current]).form.submit();
}
break;
case 38: // Вверх
current--;
if (current < 0) current = 0;
setFocus (this._elements[current]);
break;
case 40: // Вниз
current++;
if (current >= this._elements.length) current = this._elements.length - 1;
setFocus (this._elements[current]);
break;
}
return false;
}
}
Windows -> Linux: ошибка sh: ./index.cgi: not found
Перенёс скрипт из Windows в Linux, поменял первую строчку с #newlisp на #/usr/bin/newlisp.
Всё равно http-сервер пишет "not found". Через несколько минут догадался открыть файл в vim.
Оказывается, у меня было написано не #/usr/bin/newlisp, а #/usr/bin/newlisp^M.
Убрал с конца Windows-перевод строки и всё заработало.
Перекодировка также сработала нормально:
http://hilocomod.blogspot.com/2010/04/php-windows-linux.html
Всё равно http-сервер пишет "not found". Через несколько минут догадался открыть файл в vim.
Оказывается, у меня было написано не #/usr/bin/newlisp, а #/usr/bin/newlisp^M.
Убрал с конца Windows-перевод строки и всё заработало.
Перекодировка также сработала нормально:
http://hilocomod.blogspot.com/2010/04/php-windows-linux.html
воскресенье, 4 апреля 2010 г.
Обработка html в newLISP
Лутц таки убрал "анафорическую" переменную $it из replace. Теперь моя функция обработки html выглядит вот так:
; Отображает несколько html-файлов с вкраплениями кода на newLISP
(define (process-html)
(let (html (join (map read-file (args))))
(do-while (> $0)
(replace "<@((?!<@).)+?@>" html
(if (catch (eval-string (2 -2 $0)) 'result) (string result) "") 4))
(print "Content-type: text/html\r\n\r\n" html)))
CSS: отцентровать весь документ
По центру страницы идёт полоса шириной 800 пикселей. Как это сделать через CSS.
1. Прописываем стили:
2. Обёртываем данные в дивы:
1. Прописываем стили:
<style>
body { font-family: Tahoma; text-align: center;} /* для Ослика */
.wrapper { width: 800; margin: 0 auto; } /* для остальных браузеров */
</style>
2. Обёртываем данные в дивы:
<body>
<div class="wrapper">
Наши данные.
</div>
<div class="wrapper">
...
пятница, 2 апреля 2010 г.
Три добродетели программиста
Источник: http://en.wikipedia.org/wiki/Larry_Wall
Во время работы над второй редакцией книги "Программируем на Перл" Ларри Волл, вместе с Рэндаллом Л. Шварцем и Томом Христиансеном, сформулировал три добродетели программиста. Вот они.
1. Лень — качество, которое заставляет тебя прикладывать большие усилия, чтобы уменьшить общие затраты энергии. Оно заставляет тебя писать трудосберегающие программы, которые другие люди находят полезными, и документировать эти программы, чтобы тебе не приходилось отвечать на кучу вопросов пользователей.
2. Нетерпеливость — гнев, который ты испытываешь, когда компьютер ленится. Этот гнев заставляет тебя писать программы, которые не только выполняют твои команды, но и предвидят их. Или, по меньшей мере, притязают на это.
3. Высокомерие — непомерная гордость, что-то из разряда тех пороков, за которые Зевс поражает молнией. Также это качество, которое заставляет тебя писать (и поддерживать) программы, которыми другие люди будут восхщаться.
Во время работы над второй редакцией книги "Программируем на Перл" Ларри Волл, вместе с Рэндаллом Л. Шварцем и Томом Христиансеном, сформулировал три добродетели программиста. Вот они.
1. Лень — качество, которое заставляет тебя прикладывать большие усилия, чтобы уменьшить общие затраты энергии. Оно заставляет тебя писать трудосберегающие программы, которые другие люди находят полезными, и документировать эти программы, чтобы тебе не приходилось отвечать на кучу вопросов пользователей.
2. Нетерпеливость — гнев, который ты испытываешь, когда компьютер ленится. Этот гнев заставляет тебя писать программы, которые не только выполняют твои команды, но и предвидят их. Или, по меньшей мере, притязают на это.
3. Высокомерие — непомерная гордость, что-то из разряда тех пороков, за которые Зевс поражает молнией. Также это качество, которое заставляет тебя писать (и поддерживать) программы, которыми другие люди будут восхщаться.
четверг, 1 апреля 2010 г.
PuTTY и слепой синий цвет
В PuTTY из-под Windows синий цвет на чёрном фоне практически неразличим. Решение нашёл вот здесь:
Ещё хорошая идея сменить шрифт.
Я поставил себе Lucida 12.
1. Переходим в Window > Colours.
2. Ставим ANSI Blue в Red:74 Green:74 Blue:255.
3. Ставим ANSI Blue Bold в Red:140 Green:140 Blue:255.
Ещё хорошая идея сменить шрифт.
Я поставил себе Lucida 12.
Миграция PHP программы с Windows на Linux
Переводил один небольшой проект с Windows на Ubuntu. Проект был написан на быдло-PHP: аккуратно, но весьма малоопытным программистом. Вот несколько встретившихся мелочей.
1. PHP минут 15 упорно не хотел запускать index.php, предлагая вместо этого скачать phtml-файл. Дело оказалось в кэше браузера.
2. Проект хранил свои настройки в файлах с русскими именами и с русскими переменными внутри. После переконвертации всех программных и настроечных файлов из cp1251 в utf8 проект замечательно заработал. Переконвертировал я так:
find . -name "*php" -exec iconv -f cp1251 -t utf8 {} -o {}.utf8 \;
find . -iregex ".+.php$" -exec rm {} \;
find . -name '.utf8' -prune -o -exec rename 's/\.utf8$//i' {} +
3. Русские файлы прикреплялись с потерей кусков имени. Проблема оказалась в некорректной работе функции basename. Чтобы нормально заработало, пришлось добавить строку
setlocale(LC_ALL, 'ru_RU.UTF8');
4. Выяснилось, что IE, получая файлы в UTF8 через скрипт getfile.php?filename=xxx, присваивает им безумные имена. Переписал функцию, чтобы ссылка вела сразу прямо на файл, всё заработало.
1. PHP минут 15 упорно не хотел запускать index.php, предлагая вместо этого скачать phtml-файл. Дело оказалось в кэше браузера.
2. Проект хранил свои настройки в файлах с русскими именами и с русскими переменными внутри. После переконвертации всех программных и настроечных файлов из cp1251 в utf8 проект замечательно заработал. Переконвертировал я так:
find . -name "*php" -exec iconv -f cp1251 -t utf8 {} -o {}.utf8 \;
find . -iregex ".+.php$" -exec rm {} \;
find . -name '.utf8' -prune -o -exec rename 's/\.utf8$//i' {} +
3. Русские файлы прикреплялись с потерей кусков имени. Проблема оказалась в некорректной работе функции basename. Чтобы нормально заработало, пришлось добавить строку
setlocale(LC_ALL, 'ru_RU.UTF8');
4. Выяснилось, что IE, получая файлы в UTF8 через скрипт getfile.php?filename=xxx, присваивает им безумные имена. Переписал функцию, чтобы ссылка вела сразу прямо на файл, всё заработало.
среда, 31 марта 2010 г.
Установка Subversion
Поставил Subversion по вот этому HowTo:
http://habrahabr.ru/blogs/ubuntu/26117/
Ниже он же с моими комментариями.
Установка Subversion и Trac
1. Ставим trac, python и subversion:
sudo apt-get install trac libapache2-svn subversion python-subversion libapache2-mod-python
2. Включаем модуль питона:
sudo a2enmod mod_python
Настройка Subversion
1. Создаем группу для работы с SVN:
sudo groupadd svn
2. Добавляем себя (ваше имя пользователя в Ubuntu) в группу svn:
sudo usermod -a -G svn имя_пользователя
3. Добавляем apache в группу svn:
sudo usermod -a -G svn www-data
4. Создаем папку для будущего репозитория:
sudo mkdir /var/svn
5. Создаем репозиторий:
sudo svnadmin create /var/svn
6. Меняем права к папке для доступа пользователям из группы svn:
sudo chown -R www-data:svn /var/svn
7. Разрешаем на запись группе и владельцу:
sudo chmod -R g+ws /var/svn
8. Создаем пароль для доступа к папке с репозиторием, который впоследствии будет использоваться apache'ем:
sudo htpasswd -c -m /etc/apache2/svn.htpasswd имя_пользователя
Примечание Hilo: это отдельный пароль для svn, с паролем входа в систему совпадать не обязан. При создании пароля для следующего пользователя надо запускать htpasswd уже без ключа -c, который означает "создать новый файл, стерев, что как, предыдущий".
9. Теперь создадим правило для Apache для доступа к svn-репозиторию:
Alt+F2 gksu gedit /etc/apache2/conf.d/svn
<Location "/svn">
DAV svn
SVNPath /var/svn
AuthType Basic
AuthName "SVN Repo"
AuthUserFile /etc/apache2/svn.htpasswd
Require valid-user
</Location>
Примечание Hilo: я вместо этого добавил следующее в /etc/apache2/sites-available/default:
Идея в том, чтобы svn был доступен по localhost:82
10. SVN установлен! Он доступен по адресу http://localhost/svn
Примечание Hilo: sudo /etc/init.d/apache2 restart
Настройка Trac
1. Создаем папку для Trac'а :
sudo mkdir /var/trac
2. Создаем среду для работы Trac с SVN :
sudo trac-admin /var/trac initenv
3. Меняем права к папке с trac'ом :
sudo chown -R www-data:svn /var/trac
sudo chmod -R g+ws /var/trac
4. Создаем пароль для админ-доступа к папке с trac'ом, который впоследствии будет использоваться apache'ем :
sudo htpasswd -c -m /etc/apache2/trac.htpasswd имя_пользователя
5. Создадим правило для Apache для доступа к trac'у :
Alt+F2 gksu gedit /etc/apache2/conf.d/trac
<LocationMatch "/trac/login">
AuthType Basic
AuthName "Projects"
AuthUserFile /etc/apache2/trac.htpasswd
Require valid-user
</LocationMatch>
<Location /trac>
SetHandler mod_python
PythonInterpreter main_interpreter
PythonHandler trac.web.modpython_frontend
PythonOption TracEnv /var/trac
PythonOption TracUriRoot /trac
</Location>
Примечание Hilo: то же самое, повесил на порт 83, добавив в
/etc/apache2/sites-available/default вот что:
6. Trac установлен! Он доступен по адресу http://localhost/trac
Установка плагина TracWebAdmin
1. Чтобы устанавливать плагины для trac'а - нам нужен пакет "python-setuptools" :
sudo apt-get install python-setuptools
2. Переходим в папку, где лежат trac-плагины :
cd /usr/share/trac/plugins
3. Будем ставить TracWebAdmin из svn-репозитария :
sudo svn co http://svn.edgewall.org/repos/trac/sandbox/webadmin/
Примечание Hilo: после выполнения команды скачался один файл README, в котором
было предложено сделать switch. Сработало следующее:
cd webadmin
sudo svn switch http://svn.edgewall.org/repos/trac/plugins/0.10/webadmin
4. Переходим в папку с webadmin'ом и собираем его установочный egg-файл :
cd webadmin
sudo python setup.py bdist_egg
5. Переходим в папку со свежеиспеченным установщиком командой...
cd /usr/share/trac/plugins/webadmin/dist
...выводим создержимое папки...
dir
... и копируем имя egg-файла.
6. Устанавливаем TracWebAdmin :
sudo easy_install-2.5 имя_egg_файла
7. Включаем наш плагин в конфигурацию trac'а :
Alt+F2 gksu gedit /etc/trac/trac.ini
[components]
webadmin.* = enabled
http://habrahabr.ru/blogs/ubuntu/26117/
Ниже он же с моими комментариями.
Установка Subversion и Trac
1. Ставим trac, python и subversion:
sudo apt-get install trac libapache2-svn subversion python-subversion libapache2-mod-python
2. Включаем модуль питона:
sudo a2enmod mod_python
Настройка Subversion
1. Создаем группу для работы с SVN:
sudo groupadd svn
2. Добавляем себя (ваше имя пользователя в Ubuntu) в группу svn:
sudo usermod -a -G svn имя_пользователя
3. Добавляем apache в группу svn:
sudo usermod -a -G svn www-data
4. Создаем папку для будущего репозитория:
sudo mkdir /var/svn
5. Создаем репозиторий:
sudo svnadmin create /var/svn
6. Меняем права к папке для доступа пользователям из группы svn:
sudo chown -R www-data:svn /var/svn
7. Разрешаем на запись группе и владельцу:
sudo chmod -R g+ws /var/svn
8. Создаем пароль для доступа к папке с репозиторием, который впоследствии будет использоваться apache'ем:
sudo htpasswd -c -m /etc/apache2/svn.htpasswd имя_пользователя
Примечание Hilo: это отдельный пароль для svn, с паролем входа в систему совпадать не обязан. При создании пароля для следующего пользователя надо запускать htpasswd уже без ключа -c, который означает "создать новый файл, стерев, что как, предыдущий".
9. Теперь создадим правило для Apache для доступа к svn-репозиторию:
Alt+F2 gksu gedit /etc/apache2/conf.d/svn
<Location "/svn">
DAV svn
SVNPath /var/svn
AuthType Basic
AuthName "SVN Repo"
AuthUserFile /etc/apache2/svn.htpasswd
Require valid-user
</Location>
Примечание Hilo: я вместо этого добавил следующее в /etc/apache2/sites-available/default:
#
# Subversion
#
Listen 82
ServerName *:82
NameVirtualHost *:82
<VirtualHost *:82>
<Location "/">
DAV svn
SVNPath /var/svn
AuthType Basic
AuthName "SVN Repo"
AuthUserFile /etc/apache2/svn.htpasswd
Require valid-user
</Location>
</VirtualHost>
Идея в том, чтобы svn был доступен по localhost:82
10. SVN установлен! Он доступен по адресу http://localhost/svn
Примечание Hilo: sudo /etc/init.d/apache2 restart
Настройка Trac
1. Создаем папку для Trac'а :
sudo mkdir /var/trac
2. Создаем среду для работы Trac с SVN :
sudo trac-admin /var/trac initenv
3. Меняем права к папке с trac'ом :
sudo chown -R www-data:svn /var/trac
sudo chmod -R g+ws /var/trac
4. Создаем пароль для админ-доступа к папке с trac'ом, который впоследствии будет использоваться apache'ем :
sudo htpasswd -c -m /etc/apache2/trac.htpasswd имя_пользователя
5. Создадим правило для Apache для доступа к trac'у :
Alt+F2 gksu gedit /etc/apache2/conf.d/trac
<LocationMatch "/trac/login">
AuthType Basic
AuthName "Projects"
AuthUserFile /etc/apache2/trac.htpasswd
Require valid-user
</LocationMatch>
<Location /trac>
SetHandler mod_python
PythonInterpreter main_interpreter
PythonHandler trac.web.modpython_frontend
PythonOption TracEnv /var/trac
PythonOption TracUriRoot /trac
</Location>
Примечание Hilo: то же самое, повесил на порт 83, добавив в
/etc/apache2/sites-available/default вот что:
#
# Trac
#
Listen 83
ServerName *:83
NameVirtualHost *:83
<VirtualHost *:83>
<LocationMatch "/login">
AuthType Basic
AuthName "Projects"
AuthUserFile /etc/apache2/trac.htpasswd
Require valid-user
</LocationMatch>
<Location "/">
SetHandler mod_python
PythonInterpreter main_interpreter
PythonHandler trac.web.modpython_frontend
PythonOption TracEnv /var/trac
PythonOption TracUriRoot /
</Location>
</VirtualHost>
6. Trac установлен! Он доступен по адресу http://localhost/trac
Установка плагина TracWebAdmin
1. Чтобы устанавливать плагины для trac'а - нам нужен пакет "python-setuptools" :
sudo apt-get install python-setuptools
2. Переходим в папку, где лежат trac-плагины :
cd /usr/share/trac/plugins
3. Будем ставить TracWebAdmin из svn-репозитария :
sudo svn co http://svn.edgewall.org/repos/trac/sandbox/webadmin/
Примечание Hilo: после выполнения команды скачался один файл README, в котором
было предложено сделать switch. Сработало следующее:
cd webadmin
sudo svn switch http://svn.edgewall.org/repos/trac/plugins/0.10/webadmin
4. Переходим в папку с webadmin'ом и собираем его установочный egg-файл :
cd webadmin
sudo python setup.py bdist_egg
5. Переходим в папку со свежеиспеченным установщиком командой...
cd /usr/share/trac/plugins/webadmin/dist
...выводим создержимое папки...
dir
... и копируем имя egg-файла.
6. Устанавливаем TracWebAdmin :
sudo easy_install-2.5 имя_egg_файла
7. Включаем наш плагин в конфигурацию trac'а :
Alt+F2 gksu gedit /etc/trac/trac.ini
[components]
webadmin.* = enabled
Подсчитать число строк в проекте
Как подсчитать общее число строк во всех скриптах проекта?
Например, так:
Правда, я делал чуть иначе. Сначала я вывел список всех файлов в файл:
Потом убрал в файле лишнее (типа резервных копий) текстовым редактором. Потом уже направил оставшиеся файлы на wc:
Например, так:
find . -name "*cgi" | xargs wc -l
Правда, я делал чуть иначе. Сначала я вывел список всех файлов в файл:
find . -regex ".*\(lsp\|cgi\|scm\)$" > test.txt
Потом убрал в файле лишнее (типа резервных копий) текстовым редактором. Потом уже направил оставшиеся файлы на wc:
cat test.txt > xargs wc -l
вторник, 30 марта 2010 г.
PHP: парсинг лога
Базовый инструментарий для парсинга логов в PHP практически совпадает с ЛИСП’овским:
explode — разбить строку в массив
preg_match_all — найти все вхождения (в массив)
count — длина массива
array_unique — уникальные элементы массива
file — прочесть файл в массив
file_get_contents — прочесть файл в строку
Сейчас я с ужасом вспоминаю, как писал когда-то парсер логов на PHP без регулярных выражений и без массивов. Просто большим количеством циклов.
explode — разбить строку в массив
preg_match_all — найти все вхождения (в массив)
count — длина массива
array_unique — уникальные элементы массива
file — прочесть файл в массив
file_get_contents — прочесть файл в строку
Сейчас я с ужасом вспоминаю, как писал когда-то парсер логов на PHP без регулярных выражений и без массивов. Просто большим количеством циклов.
воскресенье, 28 марта 2010 г.
Ubuntu — смонтировать флешку
Ситуация: на удалённом компьютере вставлена, но не смонтирована флешка. Ребут не помогает, так как флешка была отключена через «отсоединить том» на рабочем столе Гнома.
Решение:
Решение:
sudo mkdir /media/flash
sudo mount /dev/sdb1 /media/flash -o utf8
суббота, 27 марта 2010 г.
Большие числа
Сделал мини-библиотеку для работы с большими числами (типа стозначных или пятисотзначных).
Странное дело: первое возведение тройки в тысячную степень занимает 1 секунду, а каждое следующее возведение увеличивает это время примерно на секунду, пока к пятой итерации не достигает 4,5 секунд. На этой скорости происходит стабилизация: сотое повторение занимает те же 4,5 секунды.
Подозреваю, проблема в том, что списки длиной более 100 элементов работают медленнее, чем массивы. Впрочем, так как стабилизация таки происходит, проблема некритична. Учитывая, что возведение тройки в десятитысячную степень занимает уже пять минут, следует предположить, что я отловил грань, за которой длина списка становится критичной.
А вот довесочек, проверяющий скорость:
Странное дело: первое возведение тройки в тысячную степень занимает 1 секунду, а каждое следующее возведение увеличивает это время примерно на секунду, пока к пятой итерации не достигает 4,5 секунд. На этой скорости происходит стабилизация: сотое повторение занимает те же 4,5 секунды.
Подозреваю, проблема в том, что списки длиной более 100 элементов работают медленнее, чем массивы. Впрочем, так как стабилизация таки происходит, проблема некритична. Учитывая, что возведение тройки в десятитысячную степень занимает уже пять минут, следует предположить, что я отловил грань, за которой длина списка становится критичной.
; Добить нулями num до длины len
(define (big-leading-zero len num)
(append (dup 0 (- len (length num))) num))
; Выровнять большие числа по длине
(define (big-align)
(let (len (apply max (map length (args))))
(map (curry big-leading-zero len) (args))))
; 123 -> '(1 2 3)
(define (int-to-big num)
(map int (explode (string num))))
; '(1 2 3) -> 123
(define (big-to-int big)
(int (join (apply string big))))
; '(1 2 3) -> "123"
(define (big-to-string big)
(join (map string big)))
; "123" -> '(1 2 3)
(define (string-to-big big)
(map int (explode big)))
; "Упаковать" число так, чтобы в каждом разряде сидело не больше 9
(define (big-pack num)
(let (num (reverse num) o-num '() dig nil tra 0 lead nil)
(while (or (> tra) (not (null? num)))
(set 'dig (+ (or (pop num) 0) tra))
(push (% dig 10) o-num)
(set 'tra (/ dig 10)))
(if (set 'lead (find 0 o-num <)) (lead o-num) '(0))))
; Сложить большие числа
(define (big-add)
(big-pack (map (fn (x) (apply + x)) (transpose (apply big-align (args))))))
; Умножить два больших числа
(define (big-mul-2 num1 num2)
(apply big-add
(map (fn (y) (append y (dup 0 $idx)))
(reverse (map (fn (x) (map (curry mul x) num2)) num1)))))
; Перемножить большие числа
(define (big-mul)
(apply big-mul-2 (args) 2))
; Возвести большое число в большую степень
(define (big-pow num pw)
(let (pw2 (list num))
(dotimes (x (- (length (bits pw)) 1))
(push (apply big-mul-2 (dup (if pw2 (first pw2) num) 2)) pw2))
(apply big-mul
(map (fn (x y) (if (= x "1") y '(1))) (explode (bits pw)) pw2))))
А вот довесочек, проверяющий скорость:
; Проверка скорости
(dotimes (x 1000)
(println x ": " (time (big-pow '(3) 1000))))
russian.lsp
Обновил модуль "russian.lsp".
1. Нашёл баг в функции cyr-translit: "explode" неправильно работал из-под Windows с русскими буквами. Заменил конструкцию (explode linea) на (cyr-explode linea) и всё заработало. В функции cyr-explode пришлось выяснять текущую кодировку и использовать для препарирования строки или explode или find-all ".", смотря какой из вариантов работает.
2. Нашёл мелкий баг в функции cyr-utf-win: в конце строки возвращался (char nil), то есть, "\000". На печати он был не виден, поэтому баг я раньше не замечал.
1. Нашёл баг в функции cyr-translit: "explode" неправильно работал из-под Windows с русскими буквами. Заменил конструкцию (explode linea) на (cyr-explode linea) и всё заработало. В функции cyr-explode пришлось выяснять текущую кодировку и использовать для препарирования строки или explode или find-all ".", смотря какой из вариантов работает.
2. Нашёл мелкий баг в функции cyr-utf-win: в конце строки возвращался (char nil), то есть, "\000". На печати он был не виден, поэтому баг я раньше не замечал.
; Russian encoding support by Anon. 27 Mar 2010.
; Varning: this module and codepages inside are far not complete. Some special
; symbols are not included.
;
; Functions:
;
; Usage: (cyr-win-utf "text in windows-1251 encoding")
; Decodes text from windows-1251 to utf-8
;
; Usage: (cyr-koi-utf "text in KOI8R encoding")
; Decodes text from KOI8R to utf-8
;
; Usage: (cyr-utf-win "text in utf-8 encoding")
; Decodes text from utf-8 to win-1251
;
; Usage: (url-encode-utf "В. Пупкин")
; Encodes strings for urls (utf)
; Output: "%D0%92.%20%D0%9F%D1%83%D0%BF%D0%BA%D0%B8%D0%BD"
;
; Usage: (url-encode-win "Василий Пупкин")
; Encodes strings for urls (windows-1251)
; Output: "%C2.%20%CF%F3%EF%EA%E8%ED"
;
; Usage: (cyr-rtf "\\u1055\\'3F\\u1086\\'3F\\u1089\\'3F\\u1083\\'3F")
; Decodes russian letters sequences in the .rtf file.
; Example: (cyr-rtf (read-file "report.rtf"))
;
; Usage: (cyr-rtf-r "спецификация")
; Encodes a string to the rtf russian letters format (useful for searching
; in raw-rtf). Expect problems with spaces and other \u special symbols.
;
; Usage: (cyr-translit "Щёлочь, кислота, алюминий") => "SCHyolochy, kislota, alyuminiy"
;
; Usage: (cyr-translit_ "Василий Пупкин") => "Vasiliy_Pupkin"
; Russian alphabet (33 small and 33 big letters)
(set 'cyr-alphabet (list "а" "б" "в" "г" "д" "е" "ё" "ж" "з" "и" "й" "к" "л" "м" "н" "о" "п" "р" "с" "т" "у" "ф" "х" "ц" "ч" "ш" "щ" "ъ" "ы" "ь" "э" "ю" "я" "А" "Б" "В" "Г" "Д" "Е" "Ё" "Ж" "З" "И" "Й" "К" "Л" "М" "Н" "О" "П" "Р" "С" "Т" "У" "Ф" "Х" "Ц" "Ч" "Ш" "Щ" "Ъ" "Ы" "Ь" "Э" "Ю" "Я"))
; Url-encoding for russian letters UTF
(set 'en-url-utf '(("а" "%D0%B0") ("б" "%D0%B1") ("в" "%D0%B2") ("г" "%D0%B3")
("д" "%D0%B4") ("е" "%D0%B5") ("ё" "%D1%91") ("ж" "%D0%B6") ("з" "%D0%B7")
("и" "%D0%B8") ("й" "%D0%B9") ("к" "%D0%BA") ("л" "%D0%BB") ("м" "%D0%BC")
("н" "%D0%BD") ("о" "%D0%BE") ("п" "%D0%BF") ("р" "%D1%80") ("с" "%D1%81")
("т" "%D1%82") ("у" "%D1%83") ("ф" "%D1%84") ("х" "%D1%85") ("ц" "%D1%86")
("ч" "%D1%87") ("ш" "%D1%88") ("щ" "%D1%89") ("ъ" "%D1%8A") ("ы" "%D1%8B")
("ь" "%D1%8C") ("э" "%D1%8D") ("ю" "%D1%8E") ("я" "%D1%8F") ("А" "%D0%90")
("Б" "%D0%91") ("В" "%D0%92") ("Г" "%D0%93") ("Д" "%D0%94") ("Е" "%D0%95")
("Ё" "%D0%81") ("Ж" "%D0%96") ("З" "%D0%97") ("И" "%D0%98") ("Й" "%D0%99")
("К" "%D0%9A") ("Л" "%D0%9B") ("М" "%D0%9C") ("Н" "%D0%9D") ("О" "%D0%9E")
("П" "%D0%9F") ("Р" "%D0%A0") ("С" "%D0%A1") ("Т" "%D0%A2") ("У" "%D0%A3")
("Ф" "%D0%A4") ("Х" "%D0%A5") ("Ц" "%D0%A6") ("Ч" "%D0%A7") ("Ш" "%D0%A8")
("Щ" "%D0%A9") ("Ъ" "%D0%AA") ("Ы" "%D0%AB") ("Ь" "%D0%AC") ("Э" "%D0%AD")
("Ю" "%D0%AE") ("Я" "%D0%AF")))
; Url-encoding for russian letters windows-1251
(set 'en-url-win '(("я" "%FF") ("ю" "%FE") ("э" "%FD") ("ь" "%FC") ("ы" "%FB")
("ъ" "%FA") ("щ" "%F9") ("ш" "%F8") ("ч" "%F7") ("ц" "%F6") ("х" "%F5")
("ф" "%F4") ("у" "%F3") ("т" "%F2") ("с" "%F1") ("р" "%F0") ("п" "%EF")
("о" "%EE") ("н" "%ED") ("м" "%EC") ("л" "%EB") ("к" "%EA") ("й" "%E9")
("и" "%E8") ("з" "%E7") ("ж" "%E6") ("ё" "%B8") ("е" "%E5") ("д" "%E4")
("г" "%E3") ("в" "%E2") ("б" "%E1") ("а" "%E0") ("Я" "%DF") ("Ю" "%DE")
("Э" "%DD") ("Ь" "%DC") ("Ы" "%DB") ("Ъ" "%DA") ("Щ" "%D9") ("Ш" "%D8")
("Ч" "%D7") ("Ц" "%D6") ("Х" "%D5") ("Ф" "%D4") ("У" "%D3") ("Т" "%D2")
("С" "%D1") ("Р" "%D0") ("П" "%CF") ("О" "%CE") ("Н" "%CD") ("М" "%CC")
("Л" "%CB") ("К" "%CA") ("Й" "%C9") ("И" "%C8") ("З" "%C7") ("Ж" "%C6")
("Ё" "%A8") ("Е" "%C5") ("Д" "%C4") ("Г" "%C3") ("В" "%C2") ("Б" "%C1")
("А" "%C0")))
; Url-encoding for special symbols
(set 'en-url-sym '((" " "%20") ("$" "%24") ("&" "%26") ("+" "%2B")
("," "%2C") ("/" "%2F") (":" "%3A") (";" "%3B") ("=" "%3D") ("?" "%3F") ("@" "%40")
(" " "%20") ("\"" "%22") ("<" "%3C") (">" "%3E") ("#" "%23") ("%" "%25") ("{" "%7B")
("}" "%7D") ("|" "%7C") ("\\" "%5C") ("^" "%5E") ("~" "%7E") ("[" "%5B") ("]" "%5D")
("`" "%60")))
; Encoding table utf-8
(set 'en-utf-8 '((53424 "а") (53425 "б") (53426 "в") (53427 "г") (53428 "д")
(53429 "е") (53649 "ё") (53430 "ж") (53431 "з") (53432 "и") (53433 "й")
(53434 "к") (53435 "л") (53436 "м") (53437 "н") (53438 "о") (53439 "п")
(53632 "р") (53633 "с") (53634 "т") (53635 "у") (53636 "ф") (53637 "х")
(53638 "ц") (53639 "ч") (53640 "ш") (53641 "щ") (53642 "ъ") (53643 "ы")
(53644 "ь") (53645 "э") (53646 "ю") (53647 "я") (53392 "А") (53393 "Б")
(53394 "В") (53395 "Г") (53396 "Д") (53397 "Е") (53377 "Ё") (53398 "Ж")
(53399 "З") (53400 "И") (53401 "Й") (53402 "К") (53403 "Л") (53404 "М")
(53405 "Н") (53406 "О") (53407 "П") (53408 "Р") (53409 "С") (53410 "Т")
(53411 "У") (53412 "Ф") (53413 "Х") (53414 "Ц") (53415 "Ч") (53416 "Ш")
(53417 "Щ") (53418 "Ъ") (53419 "Ы") (53420 "Ь") (53421 "Э") (53422 "Ю")
(53423 "Я") (14844052 "—") (14845078 "№")))
; Encoding table windows-1251
(set 'en-win-1251 '((255 "я") (254 "ю") (253 "э") (252 "ь") (251 "ы")
(250 "ъ") (249 "щ") (248 "ш") (247 "ч") (246 "ц") (245 "х") (244 "ф")
(243 "у") (242 "т") (241 "с") (240 "р") (239 "п") (238 "о") (237 "н")
(236 "м") (235 "л") (234 "к") (233 "й") (232 "и") (231 "з") (230 "ж")
(184 "ё") (229 "е") (228 "д") (227 "г") (226 "в") (225 "б") (224 "а")
(223 "Я") (222 "Ю") (221 "Э") (220 "Ь") (219 "Ы") (218 "Ъ") (217 "Щ")
(216 "Ш") (215 "Ч") (214 "Ц") (213 "Х") (212 "Ф") (211 "У") (210 "Т")
(209 "С") (208 "Р") (207 "П") (206 "О") (205 "Н") (204 "М") (203 "Л")
(202 "К") (201 "Й") (200 "И") (199 "З") (198 "Ж") (168 "Ё") (197 "Е")
(196 "Д") (195 "Г") (194 "В") (193 "Б") (192 "А") (185 "№")))
; Encoding table KOI8R
(set 'en-koi8r '((241 "Я") (224 "Ю") (252 "Э") (248 "Ь") (249 "Ы")
(255 "Ъ") (253 "Щ") (251 "Ш") (254 "Ч") (227 "Ц") (232 "Х") (230 "Ф")
(245 "У") (244 "Т") (243 "С") (242 "Р") (240 "П") (239 "О") (238 "Н")
(237 "М") (236 "Л") (235 "К") (234 "Й") (233 "И") (250 "З") (246 "Ж")
(179 "Ё") (229 "Е") (228 "Д") (231 "Г") (247 "В") (226 "Б") (225 "А")
(209 "я") (192 "ю") (220 "э") (216 "ь") (217 "ы") (223 "ъ") (221 "щ")
(219 "ш") (222 "ч") (195 "ц") (200 "х") (198 "ф") (213 "у") (212 "т")
(211 "с") (210 "р") (208 "п") (207 "о") (206 "н") (205 "м") (204 "л")
(203 "к") (202 "й") (201 "и") (218 "з") (214 "ж") (163 "ё") (197 "е")
(196 "д") (199 "г") (215 "в") (194 "б") (193 "а")))
; RTF-encoding (4-digit one). Warning: instead of "3f" sometimes you can see "3F".
(set 'en-rtf '(({\u1072\'3f} {а}) ({\u1073\'3f} {б}) ({\u1074\'3f} {в})
({\u1075\'3f} {г}) ({\u1076\'3f} {д}) ({\u1077\'3f} {е}) ({\u1105\'3f} {ё})
({\u1078\'3f} {ж}) ({\u1079\'3f} {з}) ({\u1080\'3f} {и}) ({\u1081\'3f} {й})
({\u1082\'3f} {к}) ({\u1083\'3f} {л}) ({\u1084\'3f} {м}) ({\u1085\'3f} {н})
({\u1086\'3f} {о}) ({\u1087\'3f} {п}) ({\u1088\'3f} {р}) ({\u1089\'3f} {с})
({\u1090\'3f} {т}) ({\u1091\'3f} {у}) ({\u1092\'3f} {ф}) ({\u1093\'3f} {х})
({\u1094\'3f} {ц}) ({\u1095\'3f} {ч}) ({\u1096\'3f} {ш}) ({\u1097\'3f} {щ})
({\u1098\'3f} {ъ}) ({\u1099\'3f} {ы}) ({\u1100\'3f} {ь}) ({\u1101\'3f} {э})
({\u1102\'3f} {ю}) ({\u1103\'3f} {я}) ({\u1040\'3f} {А}) ({\u1041\'3f} {Б})
({\u1042\'3f} {В}) ({\u1043\'3f} {Г}) ({\u1044\'3f} {Д}) ({\u1045\'3f} {Е})
({\u1025\'3f} {Ё}) ({\u1046\'3f} {Ж}) ({\u1047\'3f} {З}) ({\u1048\'3f} {И})
({\u1049\'3f} {Й}) ({\u1050\'3f} {К}) ({\u1051\'3f} {Л}) ({\u1052\'3f} {М})
({\u1053\'3f} {Н}) ({\u1054\'3f} {О}) ({\u1055\'3f} {П}) ({\u1056\'3f} {Р})
({\u1057\'3f} {С}) ({\u1058\'3f} {Т}) ({\u1059\'3f} {У}) ({\u1060\'3f} {Ф})
({\u1061\'3f} {Х}) ({\u1062\'3f} {Ц}) ({\u1063\'3f} {Ч}) ({\u1064\'3f} {Ш})
({\u1065\'3f} {Щ}) ({\u1066\'3f} {Ъ}) ({\u1067\'3f} {Ы}) ({\u1068\'3f} {Ь})
({\u1069\'3f} {Э}) ({\u1070\'3f} {Ю}) ({\u1071\'3f} {Я})
({\u160\'3f} { }) ({\u8470\'3f} {№})))
; Translit encoding
(set 'translit '(("а" "a") ("б" "b") ("в" "v") ("г" "g") ("д" "d") ("е" "e") ("ё" "yo")
("ж" "zh") ("з" "z") ("и" "i") ("й" "y") ("к" "k") ("л" "l") ("м" "m") ("н" "n")
("о" "o") ("п" "p") ("р" "r") ("с" "s") ("т" "t") ("у" "u") ("ф" "f") ("х" "h")
("ц" "c") ("ч" "ch") ("ш" "sh") ("щ" "sch") ("ъ" "y") ("ы" "y") ("ь" "y") ("э" "e")
("ю" "yu") ("я" "ya") ("А" "A") ("Б" "B") ("В" "V") ("Г" "G") ("Д" "D") ("Е" "E")
("Ё" "YO") ("Ж" "ZH") ("З" "Z") ("И" "I") ("Й" "Y") ("К" "K") ("Л" "L") ("М" "M")
("Н" "N") ("О" "O") ("П" "P") ("Р" "R") ("С" "S") ("Т" "T") ("У" "U") ("Ф" "F")
("Х" "H") ("Ц" "C") ("Ч" "CH") ("Ш" "SH") ("Щ" "SCH") ("Ъ" "Y") ("Ы" "Y") ("Ь" "Y")
("Э" "E") ("Ю" "YU") ("Я" "YA")))
; Internal function. Explodes string to bytes
(define (cortar linea)
(unpack (dup "b" (length linea)) linea))
; Internal function. Applies codetable to the list of bytes
(define (en-de-byte linea tabla (o-linea ""))
(dolist (x (cortar linea))
(push (or (lookup x tabla) (char x)) o-linea -1))
o-linea)
; Internal function. Explodes string to letters
; There can be surprises, becouse both find-all and explode
; should work (in theory) with any encoding.
(define (cyr-explode linea)
(if (= (last (parse ((set-locale) 0) ".")) "1251")
(find-all "." linea)
(explode linea)))
; Usage: (cyr-translit "Щёлочь, кислота, аллюминий") => "SCHyolochy, kislota, allyuminiy"
(define (cyr-translit linea)
(join (map (fn (x) (or (lookup x translit) x)) (cyr-explode linea))))
; Usage: (cyr-translit_ "Василий Пупкин") => "Vasiliy_Pupkin"
(define (cyr-translit_ linea)
(replace " " (cyr-translit linea) "_"))
; Usage: (cyr-rtf "\\u1055\\'3F\\u1086\\'3F\\u1089\\'3F\\u1083\\'3F")
; Decodes russian letters in the .rtf file.
(define (cyr-rtf linea)
(replace {\\u[18][0-9]+\\'3f} linea (lookup (lower-case $it) en-rtf) 1))
; Usage: (cyr-rtf-r "спецификация")
; Encodes a string to the rtf russian letters format (useful for searching
; in raw-rtf). Expect problems with spaces and other \u special symbols.
(define (cyr-rtf-r linea)
(let (letra "" efecto "")
(while (!= (set 'letra (pop linea)) "")
(push (or (lookup letra (map reverse en-rtf)) letra) efecto -1))
(replace "\\" efecto "\\\\")
(replace "'3f" efecto "'3F")))
; Usage: (url-encode-utf "Василий Пупкин")
; Encodes strings for urls (utf)
(define (url-encode-utf t-linea)
(set 't-out "")
(dostring (t-char t-linea)
(push (or (lookup (char t-char) en-url-utf)
(lookup (char t-char) en-url-sym)
(char t-char)) t-out -1))
t-out)
; Usage: (url-encode-win "Василий Пупкин")
; Encodes strings for urls (windows-1251)
(define (url-encode-win t-linea)
(set 't-out "")
(dostring (t-char t-linea)
(push (or (lookup (char t-char) en-url-win)
(lookup (char t-char) en-url-sym)
(char t-char)) t-out -1))
t-out)
; Usage: (cyr-win-utf "text in windows-1251 encoding")
; Decodes text from windows-1251 to utf-8
(define (cyr-win-utf linea)
(en-de-byte linea en-win-1251))
; Usage: (cyr-koi-utf "text in KOI8R encoding")
; Decodes text from KOI8R to utf-8
(define (cyr-koi-utf linea)
(en-de-byte linea en-koi8r))
; Usage: (cyr-utf-win "text in utf-8 encoding")
; Decodes text from utf-8 to win-1251
(define (cyr-utf-win linea)
(let (linea (cortar linea) o-linea "" f-byte nil)
(while (set 'f-byte (pop linea))
(if (or (= f-byte 208) (= f-byte 209))
(push (or (lookup (+ (mul f-byte 256) (or (pop linea) 0)) en-utf-8) "?") o-linea -1)
(if (= f-byte 226)
(push (or
(lookup (+ 14811136 (mul (or (pop linea) 0) 256) (or (pop linea) 0)) en-utf-8)
"?") o-linea -1)
(push (char f-byte) o-linea -1))))
o-linea))
; Functions commented below will be useful for adding another charsets,
; like CP866 or ISO-8859-5 or whatever
;
; Encodes one russian utf char to url-ready form
;(define (url-encode-char t-char)
; (append
; "%" (format "%X" ((unpack "b b" t-char) 0))
; "%" (format "%X" ((unpack "b b" t-char) 1))))
;
;(set 'url-charset '())
;(dolist (x cyr-alpabet)
; (push (append (list x) (list (url-encode-char x))) url-charset -1))
;
; Types list ready to insert to the code
;(set 'counter 2)
;(dolist (x en-win-1251)
; (print "(\"" (x 1) "\" \"%" (format "%X" (x 0)) "\") ")
; (if (= counter 6)
; (begin (print "\n ") (set 'counter 0)))
; (set 'counter (+ counter 1)))
пятница, 26 марта 2010 г.
rand в newLISP + Windows (RAND_MAX)
Где-то в недрах исходников newLISP есть константа RAND_MAX. Для Windows она равна 32767, для Tru64Unix , соответственно, 2147483647. Это, что как, (- (pow 2 31) 1).
Из-за этого в Windows функция rand физически не может сгенерить больше чем 32768 разных случайных чисел. Проверить это довольно просто:
Что делать, если нам таки нужно больше случайных чисел?
Лично я сделал функцию (rand_64):
Если я правильно всё рассчитал (в чём я ни разу не уверен) она умеет генерить уже не 32 тысячи, а примерно миллиард разных случайных чисел.
Из-за этого в Windows функция rand физически не может сгенерить больше чем 32768 разных случайных чисел. Проверить это довольно просто:
> (length (unique (rand 1000000 1000000)))
32767
Что делать, если нам таки нужно больше случайных чисел?
Лично я сделал функцию (rand_64):
(define (rand_64 top num)
(map (fn (x y) (/ (* (+ (* x 32768) y) top) 1073741824))
(rand 32768 num) (rand 32768 num)))
Если я правильно всё рассчитал (в чём я ни разу не уверен) она умеет генерить уже не 32 тысячи, а примерно миллиард разных случайных чисел.
Сократить список
Задача из олимпиады для детей.
Получив на входе "1,3,4,5,6,7,8,10,12,16,17,20,21,22,23,24" надо отдать на выходе "1,3-8,10,12,16-17,20-24"
Вот моё решение:
Всё бы ничего, только скорость не радует. На последовательности в 100 000 символов программа думает аж четыре минуты. А надо миллион, да...
Оптимизировал немного код и свёл его в одну функцию, убрал медленный apply/reduce:
Время обсчёта 100 000 — 0,7 секунды. Время на 1М — 7 секунд. Время на 10М — 66 секунд. Задача выполнена, не?
Получив на входе "1,3,4,5,6,7,8,10,12,16,17,20,21,22,23,24" надо отдать на выходе "1,3-8,10,12,16-17,20-24"
Вот моё решение:
(define (pack-2 n n1)
(if (= (- (int n1) (int ((parse (-7 n) "[-,]" 0) -1))) 1)
(append n "-" n1)
(append n "," n1)))
(define (compact in)
(replace "(-([^,]+-)+)" (apply pack-2 (parse in ",") 2) "-" 0))
Всё бы ничего, только скорость не радует. На последовательности в 100 000 символов программа думает аж четыре минуты. А надо миллион, да...
Оптимизировал немного код и свёл его в одну функцию, убрал медленный apply/reduce:
(define (compact in)
(replace "(-([^,]+-)+)"
(replace "([0-9]+),(?=([0-9]+))" in
(if (= (- (int $2) (int $1)) 1) (append $1 "-") $it)
0) "-" 0))
Время обсчёта 100 000 — 0,7 секунды. Время на 1М — 7 секунд. Время на 10М — 66 секунд. Задача выполнена, не?
четверг, 25 марта 2010 г.
Сумма цифр в числе
Прочёл ещё одну историю из IT-happens. Там школьникам поставили задачу подсчитать сумму цифр в числе. Хитропопый школьник заставил пользователя вводить число по одной цифре, а потом сложил все введённые цифры.
Я попробовал повторить подвиг школьника на newLISP. Программа, как и предполагал, уложилась в полстрочки:
Я попробовал повторить подвиг школьника на newLISP. Программа, как и предполагал, уложилась в полстрочки:
(define (sum-digit x) (apply + (map int (explode (string x)))))
bind, sym, save and load
Допустим, мы хотим хранить настройки нашей программы в файле, считывать их оттуда и изменять. Как и что делать:
1. Сохранение настроек
2. Загрузка настроек
Команда bind, грубо говоря, выполняет над элементам списка типа (a b) операцию (set 'a b).
3. Изменение настроек, если имя переменной известно
4. Изменение настроек, если имя переменной приходит в другой переменной
Оператор sym преобразует строку "x" в переменную 'x.
1. Сохранение настроек
> (set 'params '((var1 1) (var2 2) (var3 3)))
((var1 1) (var2 2) (var3 3))
> (save "params.lsp" 'params)
true
2. Загрузка настроек
> (load "params.lsp")
((var1 1) (var2 2) (var3 3))
> (bind params)
3
> var1
1
Команда bind, грубо говоря, выполняет над элементам списка типа (a b) операцию (set 'a b).
3. Изменение настроек, если имя переменной известно
> (pop-assoc 'var1 params)
(var1 1)
> (push '(var1 5) params)
((var1 5) (var2 2) (var3 3))
4. Изменение настроек, если имя переменной приходит в другой переменной
> (set 'text-var "var1" 'text-value "7")
"7"
> (pop-assoc (sym text-var) params)
(var1 5)
> (push (list (sym text-var) (int text-value)) params)
((var1 7) (var2 2) (var3 3))
Оператор sym преобразует строку "x" в переменную 'x.
среда, 24 марта 2010 г.
smtp (AUTH PLAIN)
Пример короткой беседы через телнет с одним моим знакомым почтовым сервером (пароль и логин пишутся в base64)
telnet smtp.testmail.ru 25
Trying 77.222.41.7...
Connected to smtp.testmail.ru.
Escape character is '^]'.
220 smtp.testmail.ru ESMTP
EHLO robot // Здесь, вероятно, должно быть robot.domain.ru
250-smtp.testmail.ru Hello robot [204.200.222.136]
250-SIZE 26214400
250-PIPELINING
250-AUTH LOGIN PLAIN
250-STARTTLS
250 HELP
AUTH LOGIN
334 VXNlcm5hbWU6 // "Username:"
cm9ib3Q= // robot
334 UGFzc3dvcmQ6 // "Password:"
MTIzNDU= // 12345
235 Authentication succeeded
MAIL FROM:// В пришедшем письме "FROM" было пусто
250 OK
RCPT TO:
250 Accepted
DATA
354 Enter message, ending with "." on a line by itself
Hello, robot!
Привет, робот!
.
250 OK id=1NuY0E-0005EH-QE
quit
221 smtp.testmail.ru closing connection
Connection closed by foreign host.
Ubuntu 10 + Java
В Ubuntu 10 мне почему-то не удалось поставить стандартную Java от Sun. Зато замечательно сработала установка некой "открытой Java":
Не знаю как другие приложения, а newLISP запустился на ней замечательно.
sudo apt-get install openjdk-6-jdk
Не знаю как другие приложения, а newLISP запустился на ней замечательно.
Клавиша Микрософт плюс шорткаты в Убунту
HowTo: Как запускать в Убунту Writer по нажатию Флаг+W и Calc по нажатию Флаг+X.
1. Система -- Параметры -- Клавиатура -- Раскладки -- Параметры раскладки -- клавиши Alt/Win -- Hyper соответствует клавишам Win
2. Система -- Параметры -- Комбинации клавиш клавиатуры -- управление окнами -- скрыть все окна и активировать рабочий стол -- Miscrosoft+D
3. sudo apt-get install compizconfig-settings-manager
4. ccsm -- General options -- commands -- key bindings -- Run command 0 -- изменить наW.
5. ccsm -- General options -- commands -- key bindings -- Run command 1 -- изменить наX.
6. ccsm -- General options -- commands -- commands -- Run command 0 -- поставить "soffice -writer"
7. ccsm -- General options -- commands -- commands -- Run command 1 -- поставить "soffice -calc"
8. ?????
9. Profit
1. Система -- Параметры -- Клавиатура -- Раскладки -- Параметры раскладки -- клавиши Alt/Win -- Hyper соответствует клавишам Win
2. Система -- Параметры -- Комбинации клавиш клавиатуры -- управление окнами -- скрыть все окна и активировать рабочий стол -- Miscrosoft+D
3. sudo apt-get install compizconfig-settings-manager
4. ccsm -- General options -- commands -- key bindings -- Run command 0 -- изменить на
5. ccsm -- General options -- commands -- key bindings -- Run command 1 -- изменить на
6. ccsm -- General options -- commands -- commands -- Run command 0 -- поставить "soffice -writer"
7. ccsm -- General options -- commands -- commands -- Run command 1 -- поставить "soffice -calc"
8. ?????
9. Profit
вторник, 23 марта 2010 г.
Простые числа
Прочёл сейчас в IT happens историю, про мальчика, который решил за 10 минут задачу "написать программу, которая печатает простые числа от 1 до 100". Его программа выглядела так:
А вот как выглядит моя "десятиминутная" программа, показывающая превосходство newLISP над другими высокоуровневыми языками программирования:
Забавно, что открыв мануал на странице с описанием функции factor, я наткнулся на готовое решение, написанное, вероятно, самим Лутцем:
Решение Лутца, что характерно, работает на 20% быстрее быстрее.
10 CLS
20 PRINT "2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97"
30 END
А вот как выглядит моя "десятиминутная" программа, показывающая превосходство newLISP над другими высокоуровневыми языками программирования:
(define (prime? x) (= (first (factor x)) x))
(println (filter prime? (sequence 2 100)))
Забавно, что открыв мануал на странице с описанием функции factor, я наткнулся на готовое решение, написанное, вероятно, самим Лутцем:
(define (primes n , p)
(dotimes (e n)
(if (= (length (factor e)) 1)
(push e p -1))) p)
(print (primes 100))
Решение Лутца, что характерно, работает на 20% быстрее быстрее.
> (time (filter prime? (sequence 2 1000000)))
2203,125
> (time (primes 1000000))
1796,875
Стандартные лог-файлы в Linux
Вот список стандартных лог-файлов в Linux (источник):
Надо отметить, что в моей Ubuntu 8.10 присутствуют не все файлы, а кое-какие логи лежат в других местах. Ещё там же предлагаются команды для просмотра лог-файлов:
Тэйл выводит последние 10 строк файла (можно изменить, набрав tail -20 filename.log). Ключ -f означает "следить за файлом". Если лог меняется, это отображается в реальном времени.
less и more -- утилиты для просмотра файла с прокруткой. Пробел -- на страницу вниз. Выход -- Q. Ключ -f в more означает "считать только логические строки". Что это значит, я не понял.
/var/log/message: General message and system related stuff
/var/log/auth.log: Authenication logs
/var/log/kern.log: Kernel logs
/var/log/cron.log: Crond logs (cron job)
/var/log/maillog: Mail server logs
/var/log/qmail/ : Qmail log directory (more files inside this directory)
/var/log/httpd/: Apache access and error logs directory
/var/log/lighttpd: Lighttpd access and error logs directory
/var/log/boot.log : System boot log
/var/log/mysqld.log: MySQL database server log file
/var/log/secure: Authentication log
/var/log/utmp or /var/log/wtmp : Login records file
/var/log/yum.log: Yum log files
Надо отметить, что в моей Ubuntu 8.10 присутствуют не все файлы, а кое-какие логи лежат в других местах. Ещё там же предлагаются команды для просмотра лог-файлов:
# tail -f /var/log/messages
Тэйл выводит последние 10 строк файла (можно изменить, набрав tail -20 filename.log). Ключ -f означает "следить за файлом". Если лог меняется, это отображается в реальном времени.
# less /var/log/messages
# more -f /var/log/messages
less и more -- утилиты для просмотра файла с прокруткой. Пробел -- на страницу вниз. Выход -- Q. Ключ -f в more означает "считать только логические строки". Что это значит, я не понял.
newLISP: запись/чтение ini-файлов
; Экранирует строку, помещая в "" и защищая спецсимволы слешами
(define (gnirts-lave x)
(1 -1 (string (list x))))
; Записывает список в .ini-файл
(define (list-ini fl lst)
(write-file fl (join
(map (fn (x) (join (map gnirts-lave x) " ")) lst) "\n")))
; Читает список из .ini-файла
(define (ini-list fl)
(map (fn (x) (map eval-string
(select (regex "([^ ]+) (.+)" x) 3 6)))
(clean null? (parse (or (read-file fl) "") "\n"))))
; Обновить строку в .ini-файле
; Usage: (update-ini "april.ini" "days" "30")
(define (update-ini fl v d)
(let (lst (ini-list fl))
(pop-assoc v lst)
(push (list v d) lst -1)
(list-ini fl lst)))
Пример ini-файла:
"path" "/var/www/main"
"len" "Салют пионерам"
"score" "Special <>\"'\"' symbols"
Примечания:
1. Пустые строки оставлять можно.
2. Комментариями (#) не заморачивался.
3. Если файла нет, вернёт пустой список/создаст файл
newLISP: имена переменных в виде текстовых строк
Нам выдают передают имя переменной в виде текстовой строки. Например:
Задача. Передать эту переменную в функцию.
Решение 1.
Решение 2.
(set 'mylist '(1 2 3 4 5))
(set 'var "mylist")
Задача. Передать эту переменную в функцию.
Решение 1.
(define (add-list x) (apply + (eval x)))
(add-list (sym var))
Решение 2.
(define (add-list-1 x) (eval-string (append "(apply + " x ")")))
(add-list-1 var)
Base64 на JavaScript
А вот цельнотянутый из webtoolkit.info код для Base64 с моим небольшим инородным вкраплением в виде Base62 кодировки.
/**
*
* Base64 encode / decode
* http://www.webtoolkit.info/
*
**/
//
// Кодирует в формат, удобный для пересылки через GET- и POST- запросы.
// Base62.encode('Смайлик (.Y.)'); => '0KHQvNCw0LnQu9C40LogKC5ZLik_e'
// Base62.decode('0KHQvNCw0LnQu9C40LogKC5ZLik_e'); => 'Смайлик (.Y.)'
//
var Base62 = {
// Кодирует данные в букво-цифро-подчёркивание
encode : function (input) {
return Base64.encode(input).replace(/\//g, "_s").replace(/\+/g, "_p").replace(/=/g, "_e");
},
// Раскодирует данные из букво-цифро-подчёркивания
decode : function (input) {
return Base64.decode(input.replace(/_s/g, "/").replace(/_p/g, "+").replace(/_e/g, "="));
}
}
var Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// public method for encoding
encode : function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = Base64._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
},
// public method for decoding
decode : function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64._utf8_decode(output);
return output;
},
// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}
Перенаправление поддомена
Как перенаправить поддомен сайта на другой IP адрес.
1. Заходим на свой хостинг, на страницу управления DNS.
2. Добавляем запись вида forum A 204.200.222.136
3. Теперь адрес forum.mysite.ru резольвится как 204.200.222.136
1. Заходим на свой хостинг, на страницу управления DNS.
2. Добавляем запись вида forum A 204.200.222.136
3. Теперь адрес forum.mysite.ru резольвится как 204.200.222.136
понедельник, 22 марта 2010 г.
JavaScript онлайн-отладчик
Простейший отладчик кода JavaScript. Нужен, чтобы по быстрому проверить какую-нибудь мелочь в JS или чтобы отладить функцию, которая работает с данными, а не с пользовательским интерфейсом.
<html>
<head>
</head>
<body>
<textarea style="width: 1000; height: 600; border: 1px solid black;" id="script"></textarea>
<br><br>
<button style="width: 1000;" onclick="eval(getElementById('script').value)">Go!</button>
</body>
</html>
Base62 (newLISP)
newLISP вариант:
JavaScript вариант:
; Кодирует в буквоцифры и подчёркивание
(define (base62-enc text)
(replace "=" (replace "+" (replace "/" (base64-enc text) "_s") "_p") "_e"))
; Раскодирует из буквоцифр и подчёркивания
(define (base62-dec text)
(base64-dec (replace "_e" (replace "_p" (replace "_s" text "/") "+") "=")))
JavaScript вариант:
var Base62 = {
// Кодирует данные в букво-цифро-подчёркивание
encode : function (input) {
return Base64.encode(input).replace(/\//g, "_s").replace(/\+/g, "_p").replace(/=/g, "_e");
},
// Раскодирует данные из букво-цифро-подчёркивания
decode : function (input) {
return Base64.decode(input.replace(/_s/g, "/").replace(/_p/g, "+").replace(/_e/g, "="));
}
}
Javascript: передать данные в скрипт
Как передать данные в файл, не прогоняя пользователя со страницы?
Пункт первый. Делаем невидимый iframe:
<iframe style='display: none' id='fieldPsFrm' src='save.cgi' width='0' height='0' align='left'></iframe>
Пункт второй. Изменяем его location на нужный:
document.getElementById('fieldPsFrm').contentWindow.location = 'save.cgi?test=test1&test2=test3';
Комментарии:
1. Вообще, наверное, надо бы делать эту хрень через AJAX.
2. Надо будет запилить отдельную функцию, чтобы передавала параметры сама.
Пункт первый. Делаем невидимый iframe:
<iframe style='display: none' id='fieldPsFrm' src='save.cgi' width='0' height='0' align='left'></iframe>
Пункт второй. Изменяем его location на нужный:
document.getElementById('fieldPsFrm').contentWindow.location = 'save.cgi?test=test1&test2=test3';
Комментарии:
1. Вообще, наверное, надо бы делать эту хрень через AJAX.
2. Надо будет запилить отдельную функцию, чтобы передавала параметры сама.
(time) и newLISP
Функция time, оказывается, считает в Linux чуть ли не микросекунды, а вот в Windows минимальный квант времени -- 1/64 секунды (15.625 ms).
Ещё в time есть вызванный переполнением int32 баг: 0x7FFFFFFF микросекунд -- это примерно 35 минут. Поэтому интервалы длиннее 35 минут считаются неправильно.
Ещё в time есть вызванный переполнением int32 баг: 0x7FFFFFFF микросекунд -- это примерно 35 минут. Поэтому интервалы длиннее 35 минут считаются неправильно.
суббота, 20 марта 2010 г.
Windows: много файлов в одной папке
Решил из любопытства повторить вот этот опыт с максимальным числом открытых файлов в Windows.
Создал скрипт, начал генерить файлы:
Первые десять тысяч файлов генерировались 123 секунды. Следующие 90 тысяч файлов -- уже 1615 секунд.
Скорость работы, в целом, меня устроила.
Получить список всех файлов в папке через (directory) —- 0.1 секунды
Отобрать 200 файлов и прочесть их -- те же 0.1 секунды.
Сделать то же самое командой "dir" -- примерно столько же.
Прочесть файл из папки -- 0 миллисекунд (!).
И всё бы ничего, да вот только, блджад, dir показал, что у меня в папке 31 169 файлов! Я вначале даже подумал было, что это предел. Однако краткое расследование показало, что это не предел, во всём виноват генератор случайных чисел.
Вот здесь:
http://www.oszone.ru/1296/
http://forum.searchengines.ru/showthread.php?t=290056
пишут, что число файлов в некорневой папке неограничено и может достигать 25 миллионов...
Для продолжения эксперимента оставлю как-нибудь компьютер на ночь создавать файлы безо всяких md5, просто по номерам (sequence 1 500000). Если предположить, что скорость не будет меняться значительно, это должно занять несколько часов. Посмотрим, как поведёт себя Windows в равных условиях, а не на детских 32k файлов.
Кстати, del. удалил эти файлы за 20 секунд.
--- Продолжение ---
Видоизменил скрипт, поставил на ночь создаваться 500 тысяч файлов. Дотерпел где-то до 100 тысяч, потом остановил скрипт. Винчестер жужжал так, как будто он сломан, больно было слушать.
На ста тысячах скорость была более-менее, но уже не фонтан.
--- Продолжение ---
Сделал скриптом из-под Ubuntu миллион файлов. Скрипт работал примерно 40 минут, жёсткий диск при этом практически не было слышно. Вроде бы -- та же папка, та же файловая система... Интересно, почему так? Возможно, Ubuntu просто пишет на диск не после каждого файла, а раз в несколько секунд?
После перезагрузки обратно в Windows начал тестировать производительность. Она, сразу скажу, меня не очень порадовала (время newLISP выдаёт в миллисекундах):
13 секунд чтобы получить список файлов в папке первый раз, 2 секунды на каждое последующее обращение к списку файлов. В целом, терпимо.
36 секунд на прочесть 2 000 файлов -- тоже, в общем, сойдёт. Получается всего лишь 18 миллисекунд на файл.
Забавно, что повторное чтение файла занимает 0 миллисекунд. Правда, я не знаю, как именно считает скорость newLISP. Подозреваю, что значениям меньше миллисекунды верить не стоит.
Создал скрипт, начал генерить файлы:
(module "crypto.lsp")
(make-dir "c:/tmp/testdir")
(change-dir "c:/tmp/testdir")
(define (rand-2k)
(join (map (fn (x) (append (string x) ": " (join
(map string (rand 10 50))))) (sequence 0 37)) "\n"))
(time (dotimes (x 10000)
(write-file (append (crypto:md5 (string (random))) ".txt") (rand-2k))))
(time (dotimes (x 90000)
(write-file (append (crypto:md5 (string (random))) ".txt") (rand-2k))))
Первые десять тысяч файлов генерировались 123 секунды. Следующие 90 тысяч файлов -- уже 1615 секунд.
Скорость работы, в целом, меня устроила.
Получить список всех файлов в папке через (directory) —- 0.1 секунды
Отобрать 200 файлов и прочесть их -- те же 0.1 секунды.
Сделать то же самое командой "dir" -- примерно столько же.
Прочесть файл из папки -- 0 миллисекунд (!).
И всё бы ничего, да вот только, блджад, dir показал, что у меня в папке 31 169 файлов! Я вначале даже подумал было, что это предел. Однако краткое расследование показало, что это не предел, во всём виноват генератор случайных чисел.
Вот здесь:
http://www.oszone.ru/1296/
http://forum.searchengines.ru/showthread.php?t=290056
пишут, что число файлов в некорневой папке неограничено и может достигать 25 миллионов...
Для продолжения эксперимента оставлю как-нибудь компьютер на ночь создавать файлы безо всяких md5, просто по номерам (sequence 1 500000). Если предположить, что скорость не будет меняться значительно, это должно занять несколько часов. Посмотрим, как поведёт себя Windows в равных условиях, а не на детских 32k файлов.
Кстати, del. удалил эти файлы за 20 секунд.
--- Продолжение ---
Видоизменил скрипт, поставил на ночь создаваться 500 тысяч файлов. Дотерпел где-то до 100 тысяч, потом остановил скрипт. Винчестер жужжал так, как будто он сломан, больно было слушать.
На ста тысячах скорость была более-менее, но уже не фонтан.
--- Продолжение ---
Сделал скриптом из-под Ubuntu миллион файлов. Скрипт работал примерно 40 минут, жёсткий диск при этом практически не было слышно. Вроде бы -- та же папка, та же файловая система... Интересно, почему так? Возможно, Ubuntu просто пишет на диск не после каждого файла, а раз в несколько секунд?
После перезагрузки обратно в Windows начал тестировать производительность. Она, сразу скажу, меня не очень порадовала (время newLISP выдаёт в миллисекундах):
> (time (directory "."))
13906,25
> (time (directory "."))
2343,75
> (time (directory "." "7.*"))
2187,5
> (length (directory "." "7.*"))
468559
> (length (directory "." "777.*"))
1999
> (time (map read-file (directory "." "777.*")))
36406,25
> (time (read-file "123_456.txt"))
31,25
> (time (read-file "123_456.txt"))
0
> (time (read-file "234_567.txt"))
15,625
> (time (read-file "345_678.txt"))
15,625
13 секунд чтобы получить список файлов в папке первый раз, 2 секунды на каждое последующее обращение к списку файлов. В целом, терпимо.
36 секунд на прочесть 2 000 файлов -- тоже, в общем, сойдёт. Получается всего лишь 18 миллисекунд на файл.
Забавно, что повторное чтение файла занимает 0 миллисекунд. Правда, я не знаю, как именно считает скорость newLISP. Подозреваю, что значениям меньше миллисекунды верить не стоит.
Linux: максимальное количество файлов в папке
Цель эксперимента: определить, насколько комфортно будет ворочаться мой Linux в папке с большим количеством файлов.
Компьютер: Intel(R) Core(TM)2 Duo CPU E7300 @ 2.66GHz
Жёсткий диск: Seagate Barracuda ES SATA 3.0Gb/s 500Gb
Файловая система: wubi/ntfs/ext3 (использует раздел Widnows)
Операционная система: Ubuntu 8.10
Фаза 1. Создание файлов
Так как свободных инодов было всего 640 тысяч, я принял решение сгенерить полмиллиона файлов. Размер файлов был выбран в 2 килобайта.
Вот скрипт (newLISP):
Время генерации файлов: 17 минут.
Фаза 2. Тест производительности
; Найти файлы по маске
(directory "." "7.*") ; 0,5с.
; Получить список вообще всех файлов
(directory ".") ; те же 0,5с.
; Отобрать 200 файлов и прочесть их из папки в память:
(map (fn (x) (read-file x)) (directory "." "6666.*")) ; Две секунды
; Прочесть один файл с известным именем:
(read-file "7777cc087494c9e6e474ac9fe6c01dfc.txt") ; 0,013 ms
; Прочесть файл из папки, в которой лежит всего несколько файлов:
(time (read-file "../ffec6ba54ea01fd951dc2433334abd0c.txt")) ; 0,014 ms
Вывод 1: скорость чтения файлов никак не зависит от того, сколько у них соседей в папке.
Вывод 2: файлы читаются мгновенно.
Вывод 3: непонятно, почему чтение 200 предварительно отобранных файлов занимает две секунды -- должно занимать полсекунды. Надо полагать, если сделать отбор имён, подождать две секунды, а потом прочесть их -- чтение займёт не полторы секунды, а несколько миллисекунд.
Фаза 3. Работа со встроенными командами Linux
3.1. Поиск через grep
grep -l "777" 5555* # 3 cекунды
3.2. ls
ls testdir > test.txt # 3 секунды (создался файл на 18 Мb)
3.3. Подсказка имени. Нажатие "Tab" для подсказки подвесило систему примерно на одну минуту, потом всё отвисло.
3.4. rm
rm * вылетел через 40 секунд с ошибкой:
"bash: /bin/rm: Слишком длинный список аргументов."
Выводы. Встроенные средства справляются с 500 тысячами файлов херовато. Самое разумное: сделать сначала ls в файл, а потом работать уже с этим файлом.
Фаза 4. Удаление файлов
(map delete-file (directory ".")) ; две минуты, под бодрое шуршание диска.
На начало эксперимента было свободно 641373 инодов из 856800. После окончания эксперимента свободных инодов стало 641279. Куда зажевались примерно 100 инодов -- неясно. Вероятно, на временные файлы операционной системы и временные файлы newLISP.
Общий вывод. Для серьёзной нагрузки 500 000 файлов -- это довольно натужно. Я бы предпочёл иметь запас по скорости раз в десять от демонстрируемого.
Впрочем, сначала я собираюсь обновить систему до 9.10 и посмотреть, насколько бодро пойдёт работа со встроенной файловой системой.
--- Продолжение ---
Проапгрейдился до 9.10. Файловая система ext4, созданная непосредственно на диске, без всяких промежуточных ntfs. Также немного упростил скрипт:
Миллион файлов на этот раз создавался 32 минуты. В общем, практически столько же, сколько и в прошлый раз. А вот тайминг (время newLISP сообщает в миллисекундах):
Как видно, скорость поиска в директории увеличилась по сравнению с ntfs в два раза, а вот скорость чтения файлов почему-то сильно скачет. В дальнейших экспериментах скорость чтения одного файла составляла 10-50 миллисекунд на первый заход и примерно 0,02 миллисекунды на чтение кэшированного файла. Файлы, напомню, имеют размер в два килобайта.
Итоговый вывод остался неизменным: работать с миллионом файлов в одной папке можно, если имена файлов известны. Если требуется поиск -- возможны варианты. Полсекунды на поиск иногда уже чересчур.
Удаление заняло примерно полчаса:
Затем тем же скриптом я успешно нагенерил минут за 40 миллион файлов для Windows:
http://hilocomod.blogspot.com/2010/03/windows.html
Компьютер: Intel(R) Core(TM)2 Duo CPU E7300 @ 2.66GHz
Жёсткий диск: Seagate Barracuda ES SATA 3.0Gb/s 500Gb
Файловая система: wubi/ntfs/ext3 (использует раздел Widnows)
Операционная система: Ubuntu 8.10
Фаза 1. Создание файлов
Так как свободных инодов было всего 640 тысяч, я принял решение сгенерить полмиллиона файлов. Размер файлов был выбран в 2 килобайта.
Вот скрипт (newLISP):
(module "crypto.lsp")
(make-dir "/home/hilo/testdir")
(change-dir "/home/hilo/testdir")
(define (rand-2k)
(join (map (fn (x) (append (string x) ": " (join
(map string (rand 10 50))))) (sequence 0 37)) "\n"))
(time (dotimes (x 500000)
(write-file (append (crypto:md5 (string (random))) ".txt") (rand-2k))))
Время генерации файлов: 17 минут.
Фаза 2. Тест производительности
; Найти файлы по маске
(directory "." "7.*") ; 0,5с.
; Получить список вообще всех файлов
(directory ".") ; те же 0,5с.
; Отобрать 200 файлов и прочесть их из папки в память:
(map (fn (x) (read-file x)) (directory "." "6666.*")) ; Две секунды
; Прочесть один файл с известным именем:
(read-file "7777cc087494c9e6e474ac9fe6c01dfc.txt") ; 0,013 ms
; Прочесть файл из папки, в которой лежит всего несколько файлов:
(time (read-file "../ffec6ba54ea01fd951dc2433334abd0c.txt")) ; 0,014 ms
Вывод 1: скорость чтения файлов никак не зависит от того, сколько у них соседей в папке.
Вывод 2: файлы читаются мгновенно.
Вывод 3: непонятно, почему чтение 200 предварительно отобранных файлов занимает две секунды -- должно занимать полсекунды. Надо полагать, если сделать отбор имён, подождать две секунды, а потом прочесть их -- чтение займёт не полторы секунды, а несколько миллисекунд.
Фаза 3. Работа со встроенными командами Linux
3.1. Поиск через grep
grep -l "777" 5555* # 3 cекунды
3.2. ls
ls testdir > test.txt # 3 секунды (создался файл на 18 Мb)
3.3. Подсказка имени. Нажатие "Tab" для подсказки подвесило систему примерно на одну минуту, потом всё отвисло.
3.4. rm
rm * вылетел через 40 секунд с ошибкой:
"bash: /bin/rm: Слишком длинный список аргументов."
Выводы. Встроенные средства справляются с 500 тысячами файлов херовато. Самое разумное: сделать сначала ls в файл, а потом работать уже с этим файлом.
Фаза 4. Удаление файлов
(map delete-file (directory ".")) ; две минуты, под бодрое шуршание диска.
На начало эксперимента было свободно 641373 инодов из 856800. После окончания эксперимента свободных инодов стало 641279. Куда зажевались примерно 100 инодов -- неясно. Вероятно, на временные файлы операционной системы и временные файлы newLISP.
Общий вывод. Для серьёзной нагрузки 500 000 файлов -- это довольно натужно. Я бы предпочёл иметь запас по скорости раз в десять от демонстрируемого.
Впрочем, сначала я собираюсь обновить систему до 9.10 и посмотреть, насколько бодро пойдёт работа со встроенной файловой системой.
--- Продолжение ---
Проапгрейдился до 9.10. Файловая система ext4, созданная непосредственно на диске, без всяких промежуточных ntfs. Также немного упростил скрипт:
(make-dir "/tmp/testdir")
(change-dir "/tmp/testdir")
(define (string-append) (apply append (map string (args))))
(define (rand-2k x)
(write-file (string-append x ".txt")
(join (map (fn (x) (append (string x) ": " (join
(map string (rand 10 50))))) (sequence 0 37)) "\n")))
(define (gen-1k y)
(dotimes (x 1000) (rand-2k (string-append y "_" x)))
(println y))
(time (map gen-1k (sequence 0 999)))
Миллион файлов на этот раз создавался 32 минуты. В общем, практически столько же, сколько и в прошлый раз. А вот тайминг (время newLISP сообщает в миллисекундах):
> (time (directory "."))
563,001
> (time (directory "." "666.*"))
572,296 ; Это полсекунды
> (length (directory "."))
1000002
> (time (map read-file (directory "." "666.*")))
9913,617 ; 5 миллисекунд на файл
> (time (map read-file (directory "." "668_.*")))
1186,284 ; 1 миллисекунда на файл
Как видно, скорость поиска в директории увеличилась по сравнению с ntfs в два раза, а вот скорость чтения файлов почему-то сильно скачет. В дальнейших экспериментах скорость чтения одного файла составляла 10-50 миллисекунд на первый заход и примерно 0,02 миллисекунды на чтение кэшированного файла. Файлы, напомню, имеют размер в два килобайта.
Итоговый вывод остался неизменным: работать с миллионом файлов в одной папке можно, если имена файлов известны. Если требуется поиск -- возможны варианты. Полсекунды на поиск иногда уже чересчур.
Удаление заняло примерно полчаса:
(map delete-file (directory "."))
Затем тем же скриптом я успешно нагенерил минут за 40 миллион файлов для Windows:
http://hilocomod.blogspot.com/2010/03/windows.html
пятница, 19 марта 2010 г.
Javascript rollover
А вот тот самый маленький ролловер, ради которого и затевался робот на Script-Fu
Примечания.
1. Вставлять ширину каждой кнопки в style не получилось: важно, что это именно ссылка и после .b_reg идёт a.
2. a:focus { outline:0; } нужен, чтобы после нажатия на див не оставалось мерзкой красной рамочки.
3. Сделав кнопки первый раз я обнаружил, что высота в 54 выбрана мной опрометчиво: надо 52. Тут-то я и порадовался, что смастерил себе робота, а не начал ресайзить/клеить дедовским способом, руками.
<!-- Кнопки -->
<style type="text/css">
/* Общие для всех кнопок */
.bcm a { display: block; height: 52px; float: left; position: absolute; top: 0px; }
.bcm a:hover { background-position: 0px -52px; }
a:focus { outline:0; }
/* По каждой кнопочке отдельно */
.b_about a { background-image: url('about.png'); width: 97px; left: 0px; }
.b_reg a { background-image: url('reg.png'); width: 190px; left: 103px; }
</style>
<div style="width: 1024; margin-bottom: 10; position: relative;">
<div class='bcm b_about'><a href='http://test.none' target="_blank"> </a></div>
<div class='bcm b_reg'><a href="javascript:Test('test');"> </a></div>
</div>
Примечания.
1. Вставлять ширину каждой кнопки в style не получилось: важно, что это именно ссылка и после .b_reg идёт a.
2. a:focus { outline:0; } нужен, чтобы после нажатия на див не оставалось мерзкой красной рамочки.
3. Сделав кнопки первый раз я обнаружил, что высота в 54 выбрана мной опрометчиво: надо 52. Тут-то я и порадовался, что смастерил себе робота, а не начал ресайзить/клеить дедовским способом, руками.
Узнать размеры PNG-файла
Функция на newLISP.
; Возвращает размеры png-файла (ширину и высоту)
(define (get-png-size file-name)
(map eval-string (map (curry append "0x")
(select (regex {49484452....(....)....(....)}
(join (map (curry format "%02X")
(unpack (dup "b" 100) (read-file file-name))))) 3 6))))
Script-Fu: resize, combine, vectors, regexp
Текущая библиотека разных моих мелких функций на Скрипт-Фу. Очень жаль, что Блогспот не даёт прикладывать файлы в виде файлов целиком.
;
; Расширения Script-Fu
;
; Возвращает список файлов из dir
; Пример: (hl-file-list "/tmp")
(define (hl-file-list dir)
(cadr (file-glob (string-append dir "/*") 0)))
; Загрузить файл jpg
; Пример: (hl-jpeg-load "/tmp/butt/video1.jpg")
; Возвращает: image
(define (hl-jpeg-load filename)
(car (file-jpeg-load RUN-NONINTERACTIVE filename filename)))
; Склеить вертикально два одинаковых по размеру файла jpg в один
(define (hl-jpeg-vcombine file1 file2 file3)
(let* (
(img1 (hl-jpeg-load file1))
(img2 (hl-jpeg-load file2))
(h-height (car (gimp-image-height img1)))
(f-width (car (gimp-image-width img1)))
; Создадим "подложку"
(img (car (gimp-image-new f-width (* h-height 2) RGB)))
; Обозначим локальные переменные
(dra) (dra-copy))
; Снимем копию дравабле первого файла и добавим её
; в верхнюю часть подложки
(set! dra-copy (car (gimp-image-get-active-layer img1)))
(set! dra-copy (car (gimp-layer-new-from-drawable dra-copy img)))
(gimp-image-add-layer img dra-copy -1)
; Добавим копию второго имаге вниз подложки
(set! dra-copy (car (gimp-image-get-active-layer img2)))
(set! dra-copy (car (gimp-layer-new-from-drawable dra-copy img)))
(gimp-image-add-layer img dra-copy -1)
(gimp-layer-set-offsets dra-copy 0 h-height)
; Сохраним изображение в file3
(set! dra (car (gimp-image-merge-visible-layers img 0)))
(file-png-save 1 img dra file3 file3 0 9 0 0 0 1 1)
; Подчистим за собой мусор
(hl-delete-images (list img img1 img2))))
; Удаляет загруженные изображения из памяти
(define (hl-delete-images image-list)
(map (lambda (x) (gimp-image-delete x)) image-list))
; Уменьшить размеры jpg. new-len: новая длина стороны
; flag: "w" или "h", смотря по какой стороне ориентироваться с размерами
(define (hl-resize-jpg file-in file-out new-len flag)
(let* (
(img (car (file-jpeg-load 1 file-in file-in)))
(dra (car (gimp-image-get-active-layer img)))
(old-h (car (gimp-image-height img)))
(old-w (car (gimp-image-width img)))
(new-h) (new-w))
; Вычислим новые размеры картинки
(if (string=? flag "w")
(begin
(set! new-w new-len)
(set! new-h (* old-h (/ new-w old-w))))
(begin
(set! new-h new-len)
(set! new-w (* old-w (/ new-h old-h)))))
; Изменим размеры jpg
(gimp-drawable-transform-scale dra 0 0 new-w new-h 0 3 FALSE 3 2)
(gimp-image-crop img new-w new-h 0 0)
; Сохраним картинку
(file-jpeg-save 1 img dra file-out file-out .95 0 0 0 " " 0 1 0 1)
; Очистим мусор
(gimp-image-delete img)))
; Создать картинку размерами x y
(define (hl-make-image x y)
(let* (
(img (car (gimp-image-new x y RGB)))
(dra (car (gimp-layer-new img x y RGB-IMAGE "Background" 100 NORMAL-MODE))))
(gimp-image-add-layer img dra -1)
(gimp-edit-clear dra)
(list img dra)))
; Выбрать область с закруглёнными краями
(define (hl-select-round img x y ox oy)
(gimp-rect-select img x y ox oy REPLACE FALSE 0)
(script-fu-selection-rounded-rectangle img FALSE 50 FALSE))
;
; Расширения TinyScheme
;
; Отрезает последние num символов от строки
(define (chop str num)
(substring str 0 (- (string-length str) num)))
; Проверяет, принадлежит ли значение списку
; Пример: (in-list? 3 '(1 2 3 4 5 6))
(define (in-list? itm lst)
(let* (
(found 0))
(for-each (lambda (x)
(if ((if (string? x) string=? =) x itm) (set! found (+ found 1))))
lst)
(if (> found 0) #t #f)))
; Возвращает уникальные значения списка
(define (unique lst)
(let* (
(out-list '()))
(for-each (lambda (x)
(if (in-list? x out-list) #f (set! out-list (append out-list (list x)))))
lst)
out-list))
; Вырезать имя файла из полного пути
; (hl-filename "/tmp/test.jpg") -> "test.jpg"
(define (hl-filename path)
(let* (
(f-len (string-length path))
(s-len 1))
(while (and
(<= s-len f-len)
(not (re-match "/" (substring path (- f-len s-len)))))
(set! s-len (+ s-len 1)))
(substring path (+ (- f-len s-len) 1))))
;
; Рабочие процедуры
;
; Загрузить кучу кнопок вида "test1.jpg" и "test2.jpg", изменить их
; размер до 54 по вертикали, склеить их попарно в файлы для rollover
; кнопок на сайте.
(define (hl-resize-buttons)
(let* (
(in-dir "/home/oleg/gimp/indir")
(out-dir "/home/oleg/gimp/outdir")
; В button-list лежит список файлов типа '("test", "about", "home")
(button-list (unique (map hl-filename (map
(lambda (x) (chop x 5)) (hl-file-list in-dir))))))
(for-each (lambda (x)
; Делаем ресайз кнопок
(hl-resize-jpg (string-append in-dir "/" x "1.jpg")
(string-append out-dir "/" x "1.jpg") 54 "h")
(hl-resize-jpg (string-append in-dir "/" x "2.jpg")
(string-append out-dir "/" x "2.jpg") 54 "h")
(hl-jpeg-vcombine
(string-append out-dir "/" x "1.jpg")
(string-append out-dir "/" x "2.jpg")
(string-append out-dir "/" x ".png")))
button-list)))
; Сделать кнопку в стиле веб-два-ноль с заданным текстом
; (hl-button "Нажмите здесь!" "Courier" "sample.png" '(224 224 255) '(80 80 255) '(255 255 255))
; Цвета (1-2-3): фон, цвет кнопки, цвет текста
(define (hl-button text font name color-1 color-2 color-3)
(let* (
(tx (car (gimp-text-get-extents-fontname text 26 0 font)))
(ty (cadr (gimp-text-get-extents-fontname text 26 0 font)))
(image (nl-make-image (+ tx 44) 44)))
; Очищаем фон, ставим бэкграунд
(gimp-context-set-foreground color-1)
(gimp-edit-fill (cadr image) 0)
(gimp-context-set-background color-1)
; Заполняем большой прямоугольник
(hl-select-round (car image) 4 4 (+ tx 30) ty)
(gimp-context-set-foreground color-2)
(gimp-edit-fill (cadr image) 0)
; Отбрасываем тень
(script-fu-drop-shadow (car image) (cadr image) 4 4 6 color-2 80 0)
; Заполняем малый прямоугольник градиентом
(hl-select-round (car image) 6 6 (+ tx 26) (- ty 12))
(gimp-blend (cadr image) FG-BG-RGB-MODE NORMAL-MODE GRADIENT-LINEAR
100 0 REPEAT-NONE FALSE FALSE 0 0 FALSE 0 (- ty 12) 0 -26)
; Добавляем текст
(gimp-context-set-foreground color-3)
(gimp-text-fontname (car image) (cadr image) 19 4 text 0 TRUE 26 1 font)
; Сохраняем файл
(gimp-image-merge-visible-layers (car image) 0)
(file-png-save 1
(car image)
(car (gimp-image-get-active-layer (car image)))
name
name
0 9 0 0 0 1 1)
; Удаляем изображение из памяти
(gimp-image-delete (car image))))
; Нарисовать карандаш 24х24 пиксела
; Работаем с векторами и кривыми Безье. Сделав всё в
; векторном виде, закрашиваем и изменяем размер с 240х240 до 24х24
; Получается а-ля-иконка
(define (hl-pencil)
(let* (
(img (hl-make-image 240 240))
(image (car img))
(layer (cadr img))
(pbody 0)
(stro 0)
(nose 0))
; Очищаем фон
(gimp-context-set-foreground '(244 244 255))
(gimp-edit-fill layer 0)
; Задаём цвет карандаша
(gimp-context-set-foreground '(0 0 255))
; Новые вектора
(set! pbody (car (gimp-vectors-new image "pencil")))
(gimp-image-add-vectors image pbody -1)
; Рисуем туловище карандаша
(set! stro (car (gimp-vectors-bezier-stroke-new-moveto pbody 180 20)))
(gimp-vectors-bezier-stroke-lineto pbody stro 70 130)
(gimp-vectors-bezier-stroke-conicto pbody stro 105 135 110 170)
(gimp-vectors-bezier-stroke-lineto pbody stro 220 60)
(gimp-vectors-bezier-stroke-conicto pbody stro 215 25 180 20)
; Закрашиваем туловище
(gimp-vectors-to-selection pbody 0 TRUE FALSE 0 0)
(gimp-blend layer FG-BG-RGB-MODE NORMAL-MODE GRADIENT-LINEAR
100 0 REPEAT-NONE FALSE FALSE 0 0 FALSE 125 75 240 240)
(gimp-selection-none image)
; Рисуем носик карандаша
(set! nose (car (gimp-vectors-new image "pencil")))
(gimp-image-add-vectors image nose -1)
(set! stro (car (gimp-vectors-bezier-stroke-new-moveto nose 70 130)))
(gimp-vectors-bezier-stroke-lineto nose stro 20 220)
(gimp-vectors-bezier-stroke-lineto nose stro 110 170)
(gimp-vectors-bezier-stroke-conicto nose stro 105 135 70 130)
; Закрашиваем носик
(gimp-vectors-to-selection nose 0 TRUE FALSE 0 0)
(gimp-blend layer FG-BG-RGB-MODE NORMAL-MODE GRADIENT-LINEAR
100 0 REPEAT-NONE FALSE FALSE 0 0 FALSE -30 30 170 230)
(gimp-selection-none image)
; Изменяем размер
(gimp-image-scale image 24 24)
(gimp-display-new image)))
;
; Отладочные участки
;
; Показываем изображение
; (gimp-display-new img)
; Список шрифтов (можно напамить на него hl-button)
; (gimp-fonts-get-list "")
; Отштриховываем вектора
; (gimp-context-set-brush "Circle (03)")
; (gimp-context-set-foreground '(0 0 255))
; (gimp-edit-stroke-vectors layer nose)
Grep: найти в файлах
Найти строку в нескольких файлах:
grep -lir "TODO" request*
-l: выводить только имена файлов с совпадениями
-r: искать рекурсивно
-i: игнорировать case
Найти строку в одном файле:
grep "video1.jpg" filelist.txt
grep -lir "TODO" request*
-l: выводить только имена файлов с совпадениями
-r: искать рекурсивно
-i: игнорировать case
Найти строку в одном файле:
grep "video1.jpg" filelist.txt
четверг, 18 марта 2010 г.
Команды/константы Script-Fu
Более-менее полный список команд найти несложно, если погуглить что-нибудь типа "file-bmp-load" "file-bz2-load".
Ещё несколько забавных команд лежит в файле .gimprc (не пользовательском, а в основном, в /etc).
Польза от все этого непотребства, впрочем, сомнительна.
Единственное, что я пока нашёл ценного, две константы:
RUN-INTERACTIVE (0)
RUN-NONINTERACTIVE (1)
Кое-что любопытное (скрипт массовой обработки фотографий):
http://svvord.livejournal.com/882.html
Надо бы, наверное, сказать тому товарищу, что вместо (define) лучше назначать переменные через let*...
Ещё несколько забавных команд лежит в файле .gimprc (не пользовательском, а в основном, в /etc).
Польза от все этого непотребства, впрочем, сомнительна.
Единственное, что я пока нашёл ценного, две константы:
RUN-INTERACTIVE (0)
RUN-NONINTERACTIVE (1)
Кое-что любопытное (скрипт массовой обработки фотографий):
http://svvord.livejournal.com/882.html
Надо бы, наверное, сказать тому товарищу, что вместо (define) лучше назначать переменные через let*...
dir или ls в Script-Fu
Сбылась мечта бесчисленных поколений. Я разобрался как делать "ls" в Script-Fu.
Второй параметр у file-glob -- кодировка возвращаемых имён. В Windows, вроде как, ни на что не влияет.
> (file-glob "c:\\tmp\\Тест\\*" 0)
(7 ("c:\\tmp\\Тест\\test.bmp" "c:\\tmp\\Тест\\test.dds" "c:\\tmp\\Тест\\test.png" "c:\\tmp\\Тест\\test1.bmp" "c:\\tmp\\Тест\\test1.gif" "c:\\tmp\\Тест\\test2.bmp" "c:\\tmp\\Тест\\tmp.sql"))
> (nth 2 (cadr (file-glob "c:\\tmp\\Тест\\*" 0)))
"c:\\tmp\\Тест\\test.png"
> (file-glob "c:\\tmp\\Тест\\*.bmp" 0)
(3 ("c:\\tmp\\Тест\\test.bmp" "c:\\tmp\\Тест\\test1.bmp" "c:\\tmp\\Тест\\test2.bmp"))
Второй параметр у file-glob -- кодировка возвращаемых имён. В Windows, вроде как, ни на что не влияет.
Отступы в коде внутри html
Вот здесь лежит любопытный обзор "indenting with html":
http://www.blooberry.com/indexdot/html/topics/indent.htm
Похоже, проще всего форматировать текст через тэг <pre>. Внутри тэга все пробелы и переводы строк сохраняются, а вот "<>" нужно заэкранивать через "<" и ">".
Кстати, "lt" расшифровывается как "less than", a gt как "greater than".
(Ещё, конечно, всегда можно настроить скрипт, который будет делать для нас форматирование через CSS).
http://www.blooberry.com/indexdot/html/topics/indent.htm
Похоже, проще всего форматировать текст через тэг <pre>. Внутри тэга все пробелы и переводы строк сохраняются, а вот "<>" нужно заэкранивать через "<" и ">".
Кстати, "lt" расшифровывается как "less than", a gt как "greater than".
(Ещё, конечно, всегда можно настроить скрипт, который будет делать для нас форматирование через CSS).
TinyScheme Manual
Взят из исходников GIMP 2.67
TinySCHEME Version 1.38
"Safe if used as prescribed"
-- Philip K. Dick, "Ubik"
This software is open source, covered by a BSD-style license.
Please read accompanying file COPYING.
-------------------------------------------------------------------------------
This Scheme interpreter is based on MiniSCHEME version 0.85k4
(see miniscm.tar.gz in the Scheme Repository)
Original credits in file MiniSCHEMETribute.txt.
D. Souflis (dsouflis@acm.org)
-------------------------------------------------------------------------------
What is TinyScheme?
-------------------
TinyScheme is a lightweight Scheme interpreter that implements as large
a subset of R5RS as was possible without getting very large and
complicated. It is meant to be used as an embedded scripting interpreter
for other programs. As such, it does not offer IDEs or extensive toolkits
although it does sport a small top-level loop, included conditionally.
A lot of functionality in TinyScheme is included conditionally, to allow
developers freedom in balancing features and footprint.
As an embedded interpreter, it allows multiple interpreter states to
coexist in the same program, without any interference between them.
Programmatically, foreign functions in C can be added and values
can be defined in the Scheme environment. Being a quite small program,
it is easy to comprehend, get to grips with, and use.
Known bugs
----------
TinyScheme is known to misbehave when memory is exhausted.
Things that keep missing, or that need fixing
---------------------------------------------
There are no hygienic macros. No rational or
complex numbers. No unwind-protect and call-with-values.
Maybe (a subset of) SLIB will work with TinySCHEME...
Decent debugging facilities are missing. Only tracing is supported
natively.
Scheme Reference
----------------
If something seems to be missing, please refer to the code and
"init.scm", since some are library functions. Refer to the MiniSCHEME
readme as a last resort.
Environments
(interaction-environment)
See R5RS. In TinySCHEME, immutable list of association lists.
(current-environment)
The environment in effect at the time of the call. An example of its
use and its utility can be found in the sample code that implements
packages in "init.scm":
(macro (package form)
`(apply (lambda ()
,@(cdr form)
(current-environment))))
The environment containing the (local) definitions inside the closure
is returned as an immutable value.
(defined? <symbol>) (defined? <symbol> <environment>)
Checks whether the given symbol is defined in the current (or given)
environment.
Symbols
(gensym)
Returns a new interned symbol each time. Will probably move to the
library when string->symbol is implemented.
Directives
(gc)
Performs garbage collection immediatelly.
(gcverbose) (gcverbose <bool>)
The argument (defaulting to #t) controls whether GC produces
visible outcome.
(quit) (quit <num>)
Stops the interpreter and sets the 'retcode' internal field (defaults
to 0). When standalone, 'retcode' is returned as exit code to the OS.
(tracing <num>)
1, turns on tracing. 0 turns it off. (Only when USE_TRACING is 1).
Mathematical functions
Since rationals and complexes are absent, the respective functions
are also missing.
Supported: exp, log, sin, cos, tan, asin, acos, atan, floor, ceiling,
trunc, round and also sqrt and expt when USE_MATH=1.
Number-theoretical quotient, remainder and modulo, gcd, lcm.
Library: exact?, inexact?, odd?, even?, zero?, positive?, negative?,
exact->inexact. inexact->exact is a core function.
Type predicates
boolean?,eof-object?,symbol?,number?,string?,integer?,real?,list?,null?,
char?,port?,input-port?,output-port?,procedure?,pair?,environment?',
vector?. Also closure?, macro?.
Types
Types supported:
Numbers (integers and reals)
Symbols
Pairs
Strings
Characters
Ports
Eof object
Environments
Vectors
Literals
String literals can contain escaped quotes \" as usual, but also
\n, \r, \t, \xDD (hex representations) and \DDD (octal representations).
Note also that it is possible to include literal newlines in string
literals, e.g.
(define s "String with newline here
and here
that can function like a HERE-string")
Character literals contain #\space and #\newline and are supplemented
with #\return and #\tab, with obvious meanings. Hex character
representations are allowed (e.g. #\x20 is #\space).
When USE_ASCII_NAMES is defined, various control characters can be
refered to by their ASCII name.
0 #\nul 17 #\dc1
1 #\soh 18 #\dc2
2 #\stx 19 #\dc3
3 #\etx 20 #\dc4
4 #\eot 21 #\nak
5 #\enq 22 #\syn
6 #\ack 23 #\etv
7 #\bel 24 #\can
8 #\bs 25 #\em
9 #\ht 26 #\sub
10 #\lf 27 #\esc
11 #\vt 28 #\fs
12 #\ff 29 #\gs
13 #\cr 30 #\rs
14 #\so 31 #\us
15 #\si
16 #\dle 127 #\del
Numeric literals support #x #o #b and #d. Flonums are currently read only
in decimal notation. Full grammar will be supported soon.
Quote, quasiquote etc.
As usual.
Immutable values
Immutable pairs cannot be modified by set-car! and set-cdr!.
Immutable strings cannot be modified via string-set!
I/O
As per R5RS, plus String Ports (see below).
current-input-port, current-output-port,
close-input-port, close-output-port, input-port?, output-port?,
open-input-file, open-output-file.
read, write, display, newline, write-char, read-char, peek-char.
char-ready? returns #t only for string ports, because there is no
portable way in stdio to determine if a character is available.
Also open-input-output-file, set-input-port, set-output-port (not R5RS)
Library: call-with-input-file, call-with-output-file,
with-input-from-file, with-output-from-file and
with-input-output-from-to-files, close-port and input-output-port?
(not R5RS).
String Ports: open-input-string, open-output-string, get-output-string,
open-input-output-string. Strings can be used with I/O routines.
Vectors
make-vector, vector, vector-length, vector-ref, vector-set!, list->vector,
vector-fill!, vector->list, vector-equal? (auxiliary function, not R5RS)
Strings
string, make-string, list->string, string-length, string-ref, string-set!,
substring, string->list, string-fill!, string-append, string-copy.
string=?, string<?, string>?, string>?, string<=?, string>=?.
(No string-ci*? yet). string->number, number->string. Also atom->string,
string->atom (not R5RS).
Symbols
symbol->string, string->symbol
Characters
integer->char, char->integer.
char=?, char<?, char>?, char<=?, char>=?.
(No char-ci*?)
Pairs & Lists
cons, car, cdr, list, length, map, for-each, foldr, list-tail,
list-ref, last-pair, reverse, append.
Also member, memq, memv, based on generic-member, assoc, assq, assv
based on generic-assoc.
Streams
head, tail, cons-stream
Control features
Apart from procedure?, also macro? and closure?
map, for-each, force, delay, call-with-current-continuation (or call/cc),
eval, apply. 'Forcing' a value that is not a promise produces the value.
There is no call-with-values, values, nor dynamic-wind. Dynamic-wind in
the presence of continuations would require support from the abstract
machine itself.
Property lists
TinyScheme inherited from MiniScheme property lists for symbols.
put, get.
Dynamically-loaded extensions
(load-extension <filename without extension>)
Loads a DLL declaring foreign procedures.
Esoteric procedures
(oblist)
Returns the oblist, an immutable list of all the symbols.
(macro-expand <form>)
Returns the expanded form of the macro call denoted by the argument
(define-with-return (<procname> <args>...) <body>)
Like plain 'define', but makes the continuation available as 'return'
inside the procedure. Handy for imperative programs.
(new-segment <num>)
Allocates more memory segments.
defined?
See "Environments"
(get-closure-code <closure>)
Gets the code as scheme data.
(make-closure <code> <environment>)
Makes a new closure in the given environment.
Obsolete procedures
(print-width <object>)
Programmer's Reference
----------------------
The interpreter state is initialized with "scheme_init".
Custom memory allocation routines can be installed with an alternate
initialization function: "scheme_init_custom_alloc".
Files can be loaded with "scheme_load_file". Strings containing Scheme
code can be loaded with "scheme_load_string". It is a good idea to
"scheme_load" init.scm before anything else.
External data for keeping external state (of use to foreign functions)
can be installed with "scheme_set_external_data".
Foreign functions are installed with "assign_foreign". Additional
definitions can be added to the interpreter state, with "scheme_define"
(this is the way HTTP header data and HTML form data are passed to the
Scheme script in the Altera SQL Server). If you wish to define the
foreign function in a specific environment (to enhance modularity),
use "assign_foreign_env".
The procedure "scheme_apply0" has been added with persistent scripts in
mind. Persistent scripts are loaded once, and every time they are needed
to produce HTTP output, appropriate data are passed through global
definitions and function "main" is called to do the job. One could
add easily "scheme_apply1" etc.
The interpreter state should be deinitialized with "scheme_deinit".
DLLs containing foreign functions should define a function named
init_<base-name>. E.g. foo.dll should define init_foo, and bar.so
should define init_bar. This function should assign_foreign any foreign
function contained in the DLL.
The first dynamically loaded extension available for TinyScheme is
a regular expression library. Although it's by no means an
established standard, this library is supposed to be installed in
a directory mirroring its name under the TinyScheme location.
Foreign Functions
-----------------
The user can add foreign functions in C. For example, a function
that squares its argument:
pointer square(scheme *sc, pointer args) {
if(args!=sc->NIL) {
if(sc->isnumber(sc->pair_car(args))) {
double v=sc->rvalue(sc->pair_car(args));
return sc->mk_real(sc,v*v);
}
}
return sc->NIL;
}
Foreign functions are now defined as closures:
sc->interface->scheme_define(
sc,
sc->global_env,
sc->interface->mk_symbol(sc,"square"),
sc->interface->mk_foreign_func(sc, square));
Foreign functions can use the external data in the "scheme" struct
to implement any kind of external state.
External data are set with the following function:
void scheme_set_external_data(scheme *sc, void *p);
As of v.1.17, the canonical way for a foreign function in a DLL to
manipulate Scheme data is using the function pointers in sc->interface.
Standalone
----------
Usage: tinyscheme -?
or: tinyscheme [<file1> <file2> ...]
followed by
-1 <file> [<arg1> <arg2> ...]
-c <Scheme commands> [<arg1> <arg2> ...]
assuming that the executable is named tinyscheme.
Use - in the place of a filename to denote stdin.
The -1 flag is meant for #! usage in shell scripts. If you specify
#! /somewhere/tinyscheme -1
then tinyscheme will be called to process the file. For example, the
following script echoes the Scheme list of its arguments.
#! /somewhere/tinyscheme -1
(display *args*)
The -c flag permits execution of arbitrary Scheme code.
Error Handling
--------------
Errors are recovered from without damage. The user can install his
own handler for system errors, by defining *error-hook*. Defining
to '() gives the default behavior, which is equivalent to "error".
USE_ERROR_HOOK must be defined.
A simple exception handling mechanism can be found in "init.scm".
A new syntactic form is introduced:
(catch <expr returned exceptionally>
<expr1> <expr2> ... <exprN>)
"Catch" establishes a scope spanning multiple call-frames
until another "catch" is encountered.
Exceptions are thrown with:
(throw "message")
If used outside a (catch ...), reverts to (error "message").
Example of use:
(define (foo x) (write x) (newline) (/ x 0))
(catch (begin (display "Error!\n") 0)
(write "Before foo ... ")
(foo 5)
(write "After foo"))
The exception mechanism can be used even by system errors, by
(define *error-hook* throw)
which makes use of the error hook described above.
If necessary, the user can devise his own exception mechanism with
tagged exceptions etc.
Reader extensions
-----------------
When encountering an unknown character after '#', the user-specified
procedure *sharp-hook* (if any), is called to read the expression.
This can be used to extend the reader to handle user-defined constants
or whatever. It should be a procedure without arguments, reading from
the current input port (which will be the load-port).
Colon Qualifiers - Packages
---------------------------
When USE_COLON_HOOK=1:
The lexer now recognizes the construction <qualifier>::<symbol> and
transforms it in the following manner (T is the transformation function):
T(<qualifier>::<symbol>) = (*colon-hook* 'T(<symbol>) <qualifier>)
where <qualifier> is a symbol not containing any double-colons.
As the definition is recursive, qualifiers can be nested.
The user can define his own *colon-hook*, to handle qualified names.
By default, "init.scm" defines *colon-hook* as EVAL. Consequently,
the qualifier must denote a Scheme environment, such as one returned
by (interaction-environment). "Init.scm" defines a new syntantic form,
PACKAGE, as a simple example. It is used like this:
(define toto
(package
(define foo 1)
(define bar +)))
foo ==> Error, "foo" undefined
(eval 'foo) ==> Error, "foo" undefined
(eval 'foo toto) ==> 1
toto::foo ==> 1
((eval 'bar toto) 2 (eval 'foo toto)) ==> 3
(toto::bar 2 toto::foo) ==> 3
(eval (bar 2 foo) toto) ==> 3
If the user installs another package infrastructure, he must define
a new 'package' procedure or macro to retain compatibility with supplied
code.
Note: Older versions used ':' as a qualifier. Unfortunately, the use
of ':' as a pseudo-qualifier in existing code (i.e. SLIB) essentially
precludes its use as a real qualifier.
Подписаться на:
Сообщения (Atom)
Архив блога
-
▼
2010
(64)
-
►
апреля
(19)
- Lock: продолжение.
- newLISP: самодельная блокировка текстовых файлов
- JavaScript: закрывать псевдопопап по Escape
- SICP: малая теорема Ферма
- Uptime в среде Windows
- SICP 1.19: Фибоначчи по логарифму
- Чётные числа
- SICP: итеративное возведение в степень
- Способы разменять монеты
- Хвостовая рекурсия
- Фриддл, ДКА и НКА
- SICP
- JavaScript: навигация стрелками и Enter
- Windows -> Linux: ошибка sh: ./index.cgi: not found
- Обработка html в newLISP
- CSS: отцентровать весь документ
- Три добродетели программиста
- PuTTY и слепой синий цвет
- Миграция PHP программы с Windows на Linux
-
►
марта
(37)
- Установка Subversion
- Подсчитать число строк в проекте
- PHP: парсинг лога
- Ubuntu — смонтировать флешку
- Большие числа
- russian.lsp
- rand в newLISP + Windows (RAND_MAX)
- Сократить список
- Сумма цифр в числе
- bind, sym, save and load
- smtp (AUTH PLAIN)
- Ubuntu 10 + Java
- Клавиша Микрософт плюс шорткаты в Убунту
- Простые числа
- Стандартные лог-файлы в Linux
- newLISP: запись/чтение ini-файлов
- newLISP: имена переменных в виде текстовых строк
- Base64 на JavaScript
- Перенаправление поддомена
- JavaScript онлайн-отладчик
- Base62 (newLISP)
- Javascript: передать данные в скрипт
- (time) и newLISP
- Windows: много файлов в одной папке
- Linux: максимальное количество файлов в папке
- Javascript rollover
- Узнать размеры PNG-файла
- Script-Fu: resize, combine, vectors, regexp
- Grep: найти в файлах
- Команды/константы Script-Fu
- dir или ls в Script-Fu
- Отступы в коде внутри html
- TinyScheme Manual
-
►
апреля
(19)