Придумал новый алгоритм для лоченья файлов. Где можно наколоться пока не вижу. Но опыт, конечно, покажет. Также у меня всегда остётся запасной вариант: 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-файл.
среда, 21 апреля 2010 г.
вторник, 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, присваивает им безумные имена. Переписал функцию, чтобы ссылка вела сразу прямо на файл, всё заработало.
Подписаться на:
Сообщения (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
-
▼
апреля
(19)