xaprb за замечание.Я неправильно расставил кавычки во втором запросе.Как должно было быть: date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000' AND accomodation=1 Как стало: date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000 AND accomodation=1' Обратите внимание на кавычку после accomodation=1:)
![]() |
|
Comenteaza
Стыжусь
Как мне не стыдно. В прошлом посте я допустил ошибку. Спасибо ![[info]](http://l-stat.livejournal.com/img/userinfo.gif?v%3D91.6)
xaprb за замечание.Я неправильно расставил кавычки во втором запросе.Как должно было быть: date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000' AND accomodation=1 Как стало: date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000 AND accomodation=1' Обратите внимание на кавычку после accomodation=1:)
xaprb за замечание.Я неправильно расставил кавычки во втором запросе.Как должно было быть: date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000' AND accomodation=1 Как стало: date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000 AND accomodation=1' Обратите внимание на кавычку после accomodation=1:)
Запрос, как много в этом звуке...
Разбираюсь на работе с базами данных. Краем глаза читаю «High performance MySQL». Решаю опробовать полученные навыки на практике. Хм...Есть у нас таблица, разбитая на пратиции по неделям:CREATE TABLE `price_detail` ( `accomodation` int(11) NOT NULL, `date` datetime NOT NULL, `price` int(11) NOT NULL, `room_id` int(11) NOT NULL, KEY `idx_price_date` (`date`) USING BTREE, KEY `idx_price` (`price`) USING BTREE, KEY `idx_price_accomodation` (`accomodation`) USING BTREE) PARTITION BY RANGE (TO_DAYS(`date`)) (PARTITION p20080101 VALUES LESS THAN (733407), PARTITION p20080108 VALUES LESS THAN (733414),/* и так далее — на целый год */Нда... И индексы тож...На каждую неделю у нас ни много ни мало — 26 миллионов записей в худшем случае. Для тестирования выбран именно худший случай.Смотрим, как у нас будет работать выборка из этой базы:Запрос первый:
explain partitions
select
accomodation
from
price_detail
where
accomodation=1 and date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: price_detail
partitions: p20080108,p20080115
type: ref
possible_keys: idx_price_date,idx_price_accomodation
key: idx_price_accomodation
key_len: 4
ref: const
rows: 40292
Extra: Using where
Запрос второй:
explain partitions
select
accomodation
from
price_detail
where
date BETWEEN '2008-01-01 12:00:00.000' AND '2008-01-14 12:00:00.000 AND accomodation=1'\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: price_detail
partitions: p20080108,p20080115,p20080122,p20080129,p20080205,p20080212,p20080219,
p20080226,p20080304,p20080311,p20080318,p20080325,p20080401,p20080408,
p20080415,p20080422,p20080429,p20080506,p20080513,p20080520,p20080527,
p20080603,p20080610,p20080617,p20080624,p20080701,p20080708,p20080715,
p20080722,p20080729,p20080805,p20080812,p20080819,p20080826,p20080902,
p20080909,p20080916,p20080923,p20080930,p20081007,p20081014,p20081021,
p20081028,p20081104,p20081111,p20081118,p20081125,p20081202,p20081209,
p20081216,p20081223,p20081230,p20090106,p20090113,p20090120,p20090127
type: ALL
possible_keys: idx_price_date
key: NULL
key_len: NULL
ref: NULL
rows: 274845000
Extra: Using where
Звезда в шоке.Ушел читать covering index...ЗАПОЗДАЛЫЙ АПДЕЙТ. Во тором случае забыл просто кавычку после второй даты закрыть :) Что оно в итоге искало — хз :)
Что в вакансии тебе моей...
Набираем людей на работу.Во первых строках вакансии пишем: «требуется хорошее или отличное знание РНР»Приходит письмо: «РНР я только начинаю изучать...» Может, я чего-то не понимаю в этой жизни? :)
Сотня мудрецов...
via http://andrzejn.livejournal.com/1152676.html- Сотня мудрецов знают, что нечто невозможно. Потом находится один дурак, который этого не знает...- И он-то и делает открытие?- Нет, но мудрецы ещё долго потом вспоминают его с умилением.
Размышлизмы
Как и обещал, размышления1. Во-первых, сопоставление с образцом позволяет быстро и достаточно безболезненно описывать требуемое. Например, возьмем правила:{field}
{field, Func}
{field, {'=', field2}}
Честно говоря, я слабо себе представляю, как это обработать if'ами. Думаю, все это пришлось бы оборачивать в какой-нибудь класс Rule и наследовать от него классы RuleField, RuleFunc, RuleOperator и так далее. Используя сопоставление с образцом, такие правила разребаются очень просто:%%{field}
validate_rule({FieldName}) ->
ok.
%%{field, Func}
validate_rule({FieldName, Func}) when is_function(Func) ->
ok.
%%{field, {'=', field2}}
validate_rule({FieldName, {Operator, FieldName2}}) ->
ok.
Правда, такая легкость приводит (меня, во всяком случае) к пункту 2:2. Индусокод. Эта легкость в обработке сложных конструкций моет привести к тому, что начинаешь просто писать функции, их обрабатывающие, вместо того, чтобы остановиться и подумать - а оно мне надо?В моем случае это вылилось в то, что я вроде как бы знал, что я хочу в функцию передать. Хорошо, это я обработал. А возвращать что будем? А что с результатами делать будем?В итоге, в самом начале функция возвращала дикую конструкцию такого вида:
[[Field, [Error1, Error2]], [Field2, [Error3, Error4]]]
то есть глубокий список списков. Это сейчас она возвращает более-менее proplist. С третьей попытки :)Этот же подход в стиле "вау, посмотри, что я могу обработать, а что вернем - неважно" привел к тому, что перед возвращением значения происходит следующая обработка:
lists:filter(
fun(Elem) ->
case
Elem of {} -> false;
{_, []} -> false;
_ -> true
end
end,
lists:flatten(validate1(A, ValidationRules, [])))
Дадада. Вычищаем список от нежелательных элементов. Вам страшно? Мне - да. Мне еще и грустно :(На этом мысль закончилась...
Валидация для erlyweb
Накалякал тут функцию валидации для Erlyweb. Сначала описание работы, а потом, может быть, о самой функции :) Работает примерно так:Создаем форму с четырьмя полями:- login- password- pasword_repeat- emailСтандартная форма регистрации то бишь. Естественно, нам нужно провести валидацию полей следующим образом:- login должен быть 4-16 символов- password должен быть 4-16 символов- password должен быть равен password_repeat- email должен быть правильным адресомСтандартные функции валидации для этого не подходят. Но подойдет моя :)Предположим, у нас есть функция process_signup, которая принимает на вход стандартную структуру yaws_arg. Тогда валидация будет выглядеть следующим образом:process_signup(A) ->
F = fun(A, Field) ->
{ok, Val} = yaws_api:postvar(A, Field),
L = string:len(Val),
if
L < 4 orelse L > 16 ->
{Field, length};
true ->
{}
end
end,
EmailCheck = fun(Args, Field2) ->
{ok, Email} = yaws_api:postvar(Args, Field2),
Match = regexp:match(Email, "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+"),
Match /= nomatch
end,
%% магия тут:
buktu_form:validate(A, [
{login, F},
{email, EmailCheck},
{password, [{'=', password_repeat}, F]}
]).
Таким образом, если вообще ни одно поле введено не будет, то мы получим:[{login,invalid_field},
{email,invalid_field},
{password,[{invalid_fields,[password,password_repeat]},
invalid_field]}]
Если введем поля, не соответствующие критериям, то:[{login,length},
{email,invalid_value},
{password,[{not_equal,password_repeat},
length]}]
Callback-функция, которую можно передавать для валидации, может возвращать:кортеж типа {FieldName, Error}true, если поле прошло валидацию или false в противном случае (тогда функция валидации вернет кортеж {FieldName, invalid_value})любое значение Value, которое будет трансформировано в {FieldName, Value} если любое из полей в правиле не существует или не содержит значение, то для каждого такого правила функция валидации вернет:invalid_field если поле сравнивается со значением{invalid_fields, [field1, field2]} если поле сравнивается с другим полемЕсли необходимо передать несколько правил, достаточно передать их в списке. Если достаточно проверить наличие поля, можно передать кортеж, содержащий только название поля:Проверить, существует ли такое поле{field_name} Сравнить поле field_name с полем field2_name (также можно использовать '=', '/=', '<', '=<', '>', '>=' ){field_name, {'=', field2_name}}Сравнить значение поля с любым другим значением: {field_name, {'=', Value}}Использовать callback-функцию (function/2, первый параметр - yaws_arg, второй - название поля). Функция может быть лямбдой или любой функцией из любого модуля в виде module:function/2 или {module, function}{field_name, F}Использовать callback-функцию с дополнительным значением (function/3, первый параметр - yaws_arg, второй - название поля, третий - значение). Функция может быть лямбдой или любой функцией из любого модуля в виде module:function/3 или {module, function}{field_name, {F, Value}}Функция валидации возвращает proplist в виде:[{FieldName(), Errors()}]гдеFieldName() = atom()
Errors() = Error() | [Error()]
Error() = любое_пользовательское_значение | absent | invalid_value |
invalid_field | {invalid_fields, [FieldName(), FieldName()]} |
ComparisonError()
ComparisonError() = {not_equal, value_or_field} | {equal, value_or_field} |
{not_greater, value_or_field} | {not_less, value_or_field} |
{not_greater_or_equal, value_or_field} | {not_less_or_equal, value_or_field} |
Опрос о журнале по декларативному программированию
darkus проводит опрос о целесообразности создания журнала (электронного или печатного) по декларативному программированию:http://users.livejournal.com/_darkus_/422028.html
Пристрелите
Кто-нибудь, пристрелите меня :)Я ухитрился собственноручно сдвинуть с места один из самых старых багов в Firefox'е: https://bugzilla.mozilla.org/show_bug.cgi?id=69230 (см. комментарий 86). Что с этим "счастьем" делать - ума не приложу :) А ведь мне еще собирать мозиллу из сырцов придется...Ля-ля-ля, я сошла с ума (с) :))
Erlang Bit Syntax
Решил я посмотреть, насколько все легко и просто в Эрланге с двоичными данными, как рассказывается.
Вернее, так. Один мой товарищ возился с форматом OMA, использующемся в аудиоплеерах Sony. Единственно отличие этого формата от других, человеческих, вроде MP3 в том, что он не документирован, а та информация, что об этом формате есть противоречит самому же формату :)
Добрые люди разобрались, что в этом формате к чему и написали программу на Java для работы с ним. Эта программа лежит здесь: http://dmitriid.com/files/projects/erlang/OMA.zip. Там же валяется документация по директориям на плеерах Sony, а также информация о собственно формате OMA.
Формат файла можно увидеть здесь: http://dmitriid.com/files/projects/erlang/OMA.html. По виду прост до безобразия. Ну-с, посмотрим, как с ним справится Erlang.
Первая же наприятность меня ждала в приложенном в качестве примера файле trunk/dist/OMGAUDIO/10F00/10000001.OMA. По правилам он должен был начинаться с
"E" "A" "3" 3 0 0 0 0 17 76 "T" "I" "T" "2" 0 0
Агащазблин. Начинается он с
"e" "a" "3" 3 0 0 0 0 17 "v" "G" "E" "O" "B" 0 0
И до TIT2 еще ого сколько пилить и пилить. Hex-редактор в руки, и вперед, сравнивать спеку с файлом. Вроде, указанные в спеке тэги есть, но не обязательно на своих местах. Вывод? Будем побайтово продвигаться по файлу и собирать тэги до тех пор, пока не наткнемся на конец заголовка.
Забегая вперед, скажу, что я смухлевал. Смухлевал потому, что я не читаю формат файла, а останавливаюсь на
"E" "A" "3" 2 0 60 ff ff
Итак, код
Открываем и считываем файл
parse_header(File) ->
case file:open(File, [read, binary, raw]) of
{ok, S} ->
{ok, Header} = file:pread(S, 0, 16#0c60),
H = case read_header(Header) of
error ->
{error, invalid_header};
Data ->
Data
end,
file:close(S),
H;
_ ->
{error, file_cannot_be_opened}
end.
Ну, тут все легко. Открываем файл. Считываем его. Вызываем функцию read_header для, собственно, парсинга заголовка и возвращаем значение, полученное из этой функции.
Функция read_header проста до безобразия
read_header(<<$e, $a, $3, Rest/binary>>) ->
Data = decode_header(Rest, []),
Data;
read_header(_) ->
error.
Проста он до безобразия своим паттерн матчингом. Если входящий кусок начинается с
"e" "a" "3"
то вызывается верхняя функция, а если нет, то нижняя.
Само дейстивие по разбору заголовка находится в нескольких функциях decode_header, которые занимаются разбором какого-то одного, своего куска заголовка. Вот типичный пример:
% Title
decode_header(<<$T, $I, $T, $2, _:2/binary,
Var:2/integer-unit:8, _:4/binary,
Rest/binary>>, L) ->
TitleLength = Var - 2,
<<Title:TitleLength/binary, Rest2/binary>> = Rest,
decode_header(Rest2, [L|{title, Title}]);
Что здесь происходит? Для этого надо перед глазами держать спеку.
Итак. Название песни записывается так
T I T 2 0 0 Var1 Var1 0 0 2 TitleString
где TitleString - это [0x0 String]. Что мы, собственно, и записали в паттерне:
T $T
I $I
T $T
2 $2
0 _:2/binary просто пропускаем два нуля
0
Var1 Var:2/integer-unit:8 число, состоящее из двух 8-битовых байтов
Var1
0 _:4/binary просто пропускаем 0 0 2 0
0
2
0
TitleString Rest/binary
Теперь нам осталось выцепить из Rest название песни. Опять таки опытыным путем выяснено, что, в целом, длина названия в нашем случае - это Var - 2, после чего мы выдираем само название опять же паттерн матчингом:
TitleLength = Var - 2,
<<Title:TitleLength/binary, Rest2/binary>> = Rest,
Таким образом, все, что осталось сделать, это записать парсинг всех тэгов согласно правилам и протестировать на реальном файле. Реальный файл подкидывает подляну в виде игнорирования (пусть и неофициальной, но) спецификации и сует тэги куда угодно, добавляя некоторое количество неизвестных тэгов. Поэтому decode_header поплняется еще двумя функциями:
decode_header(<<>>, L) ->
L;
decode_header(Bin, L) ->
{_, NewBin} = split_binary(Bin, 1),
decode_header(NewBin, L).
Мы по байту перемещаемся вперед по заголовку. Если мы натыкаемся на известный тэг, его перехватит decode_header с соответствующим паттерн-матчингом. Если мы дошли до конца заголовка, процесс завершится на decode_header(<<>>, L). Если ни того ни другого не произошло, мы переместимся еще на один байт вперед и все повторится снова.
Полностью весь файл доступен здесь: http://erltag.googlecode.com/svn/trunk/src/oma.erl. Кто хочет потренироваться на кошках, качайте http://erltag.googlecode.com/files/erltag-release.zip(6.1 MB). Там в папке test есть аудиофайл.
Ах да, код не претендует на эффективность и не показывает, как правильно писать программы на Эрланг'е. Скорее всего, так писать не надо :)
Programming Erlang
Я ее наконец-то получил:


В Молдову она пришла 21-го июля. Из таможни вышла 25-го. И только сегодня я заметил приглашение забрать посылку из местного почтового отделения :)Ура, товарищи!Теперь осталось найти время на ее изучение...



В Молдову она пришла 21-го июля. Из таможни вышла 25-го. И только сегодня я заметил приглашение забрать посылку из местного почтового отделения :)Ура, товарищи!Теперь осталось найти время на ее изучение...
Побольше языков, хороших и разных!
В пятницу взялся за Python.
К Питону отношение окружающих разнится от "а что там учить" до "все никак руки не доходят" до "блин, сложный язык".
У меня руки дошли по одной простой причине. У меня есть куча фотографий различного (большого) размера, которые надо представить в трех-четырех различных размерах (как это делается, например, на Flickr'е или на Яндекс.Фотках), да еще составить уменьшенные изображения (thumbnails), да еще залить все это дело на хостинг (Amazon S3) и залить информацию о загруженных файлах в базу данных.
Желательно автоматически.
Началось все с того, что я где-то увидел, что Python Imaging Library - это лучшая библиотека для работы с изображениями. И действительно:
import Image
im = Image.open('путь/к/изображению')
im.thumbnail((800, 800), Image.ANTIALIAS)
im.save('путь/к/изображению')
Особо гениальна строчка im.thumbnail((800, 800), Image.ANTIALIAS). Если у нас изображение 1024х768, то на выходе мы получим не искореженный 800х800, а пропорционально уменьшенный 800x600.
В комбинации с MySQLDb и Boto требуемое мне решение просто до безобразия. Но не в этом дело.
Как оказалось, знакомство, пусть даже шапочное, с большим количеством языков программирования дает не просто выигрыш, а гигантскую фору в изучении нового языка. Напомню, что галопом по европам я "знаком" с такими языками как Lisp, Haskell, Erlang... И что же мне это дало применительно к Питону? А дает это мне моментальное усваивание и понимание следующего:
Примечание: В примерах за код на хаскеле ответственности не несу :)
Кортежи (tuples) и destructuring assignment
Кортеж - это список фиксированной длины, состоящий из разнородных объектов:
# Python tuple
(1, 2, 'hello')
-- Haskell tuple
(1, 2, "hello")
%% Erlang tuple
{1, 2, "hello"}
Похоже, не так ли :)
А теперь поразбиваем кортежи на переменные:
# Python
# А станет равно 1
# В станет равно 2
# С станет равно 'hello'
A, B, C = (1, 2, 'hello')
-- Haskell
-- a станет равно 1
-- b станет равно 2
-- c станет равно 'hello'
(a, b, c) = (1, 2, "hello")
%% Erlang
%% А станет равно 1
%% В станет равно 2
%% С станет равно 'hello'
{A, B, C} = {1, 2, 'hello'}
Конечно, принцип работы совершенно разный. В Хаскеле и Эрланге - это сопоставление с образцом (patern matching), которого нет в Питоне, но так как выглядит это почти одинаково, то и принимается и понимается моментально. Кстати, в РНР есть похожая вещь:
// А станет равно 1
// В станет равно 2
// С станет равно 'hello'
list($A, $B, $C) = array(1, 2, 'hello')
Что, конечно, опять совсем не то и принцип работы совсем другой :) Но вот, что интересно. Привыкнув к такого рода присвоениям в Эрлнге или Хаскеле, начинаешь чаще использовать list в РНР и моментально схватываешь суть присвоений в Питоне. Потому что удобно.
Именованные параметры
Об именованных параметрах я как-то уже растекался мыслью по древу. Как оказалось, в Питоне они тоже есть, что не может не радовать.
# определяем функцию
def myfunc(param='', another_param=1)
print param, another_param
# вызываем функцию
# напечатает hello 5
myfunc(another_param=5, param='hello')
# напечатает hello 1
myfunc(param='hello')
# напечатает 1
myfunc()
Анонимные функции (лямбды)
# Python
# определяем список
li = [1, 2, 3, 5, 9, 10, 256, -3]
# выведем из него только четные
li_even = filter(lambda item: item % 2 == 0, li)
Весьма похоже на аналоги:
-- Haskell
-- определяем список
li = [1, 2, 3, 5, 9, 10, 256, -3]
-- выведем из него только четные
li_even = filter (\item -> (mod item 2) == 0) li
%% Erlang
%% определяем список
Li = [1, 2, 3, 5, 9, 10, 256, -3]
%% выведем из него только четные
Li_even = lists:filter(fun(Item) -> Item rem 2 == 0 end, Li)
List comprehensions
Помните, как списки в математике составляются? Ну, например:
S = [1, 2, 3, 4]
M = {x | x in S, x even}
M - это все x, где x - из набора S и x - четное. В хороших языках есть возможность составлять списки практически по-математически. Называется это list comprehensions. Единственный минус в питоне - это синтаксис. Далее примеры из анонимных функций, только с list comprehensions:
# Python
# определяем список
li = [1, 2, 3, 5, 9, 10, 256, -3]
# выведем из него только четные
li_even = [x for x in li if x % 2 == 0]
-- Haskell
-- определяем список
li = [1, 2, 3, 5, 9, 10, 256, -3]
-- выведем из него только четные
li_even = [x | x <- li, (mod x 2) == 0]
%% Erlang
%% определяем список
Li = [1, 2, 3, 5, 9, 10, 256, -3]
%% выведем из него только четные
Li_even = [X || X <- Li, X rem 2 == 0]
В общем, благодаря знакомству с разными языками программирования новый язык программирования (из более-менее мейнстримных, не K :)) ) оказывается не просто легким, а очень легким и осваивается... ну, за 15 минут.
Update: Поправлен код на Хаскеле. Спасибо ![[info]](http://l-stat.livejournal.com/img/userinfo.gif?v%3D91.6)
deni_ok
deni_ok
Pragmatic Erlang
Почти 60 долларов и книженция моя. Joe Armstrong. "Programming Erlang. Software for a Concurrent World" моя. Пока только в PDF-варианте. Потихоньку буду читать...Уря! :)
PHP и Unicode. Апдейт
Все забываю это написать. Ткнули в это и на работе и здесьРешение к предыдущей проблеме с версии 4.4.0 выглядит так:
$input = "Это текст /// This is a text";
$pattern = '/(\pL+)/u';
preg_match_all($pattern, $input, $arr);
print_r($arr);
Array
(
[0] => Array
(
[0] => Это
[1] => текст
[2] => This
[3] => is
[4] => a
[5] => text
)
[1] => Array
(
[0] => Это
[1] => текст
[2] => This
[3] => is
[4] => a
[5] => text
)
)
Читайте доки, господа. Они рулят :) Pattern Syntax (Unicode character properties)Pattern Modifiers (/u)
PHP и Unicode
РНР и Юникод - это песнь, что стону подобна. Потому что эти два понятия несовместимы...ЗадачаНа вход подается текст, состоящий из букв, цифр, и разных символов, таких как запятые, скобки, амперсанды и прочая и прочая.Необходимо из этого текста выделить только "слова", то есть последовательности символов, состоящих из букв и цифр. Любой символ, неявляющийся тем или дргим, считается разделителем слов.ДанныеЭто текст /// This is a textРешениеЭтап первыйВ PCRE (Perl-compatible regular expressions) есть два великолепных, скажем, мета-символа:\w - любой символ, являющийся "словом"\W - любой символ, не являющийся "словом"где "слово" - это любая буква, цифра или знак подчеркивания. Также, в зависимости от расположения луны и солнца на небе, времени суток и часового пояса, а также количества родинок на левой ягодице пользователя, \w может подцепить некоторые символы вне ASCII - такие, как ç, é и т.п. В документации написано, что это зависит от текущей локали, но кого они пытаются надурить? Подзабыв РНР и положившись на волю случая, первыая попытка решения задачи была, естественно, такой:$pattern = '/([\w]+)/';
preg_match_all($pattern, $input, $arr);
Array
(
[0] => Array
(
[0] => This
[1] => is
[2] => a
[3] => text
)
[1] => Array
(
[0] => This
[1] => is
[2] => a
[3] => text
)
)
Ндя.. РНР считает, что русский текст - это не "слова". Зато английский (или, вернее, ASCII) идет на ура. Ну не гады, а?Этап второйЭто решение у меня работало и работает в тупой парсилке man-файлов для MediaWiki. Там, правда, не надо было разбивать текст на слова...Если русский текст для РНР - это не текст вовсе, то скомбинируем \w и \W:$pattern = '/([\w|\W]+)/';
preg_match_all($pattern, $input, $arr);
Array
(
[0] => Array
(
[0] => Это текст /// This is a text
)
[1] => Array
(
[0] => Это текст /// This is a text
)
)
А ну да, оно же жадное (greedy). Убираем жадность:$pattern = '/([\w|\W]+)/U';
preg_match_all($pattern, $input, $arr);
Array
(
[0] => Array
(
[0] => Ð
[1] =>
[2] => Ñ
[3] => ‚
[4] => Ð
[5] => ¾
[6] =>
[7] => Ñ
Мама, роди меня обратно...Верно. Если мы нежадно идем побайтово по тексту, то Юникод побоку и все считается "не словом". Что ж делать-то...Этап третийПоразмыслим. Что у нас слова? Это слова/не слова, за которыми идут разделители слов. Тьфу. Запутаться можно :) Разделителями слов у нас являются всякие символы вроде {+=, пробелы, переводы строк. А в рег. выражения можно вставлять их шестнадцатиричные значения. И еще указывать, что мол с такого по такое значение. Эврика!ASCII Table в зубы. Знак отрицания шаблона, ^, тоже в зубы.$pattern = '/([\w|\W]+)([\x20-\x2F|\x3A-\x40|\x5B-\x60|\x7B-\x7F|\s|\n|\r|\f]+)/U';
preg_match_all($pattern, $input, $arr);
Array
(
[0] => Array
(
[0] => Это
[1] => текст
[2] => //
[3] => /
[4] => This
[5] => is
[6] => a
)
[1] => Array
(
[0] => Это
[1] => текст
[2] => /
[3] => /
[4] => This
[5] => is
[6] => a
)
[2] => Array
(
[0] =>
[1] =>
[2] => /
[3] =>
[4] =>
[5] =>
[6] =>
)
)
Блин. Ну да. Одно или более слово/не слово, за которым идет одно или более слово/не слово. Все логично... И, соответственно, последнее слово обрезается. Что делать-то?Может, это:$pattern = '/([\w|\W]+)([\x20-\x2F|\x3A-\x40|\x5B-\x60|\x7B-\x7F|\s|\n|\r|\f]+)([\x20-\x2F|\x3A-\x40|\x5B-\x60|\x7B-\x7F|\s|\n|\r|\f]*)/U';
preg_match_all($pattern, $input, $arr);
Array
(
[0] => Array
(
[0] => Это
[1] => текст
[2] => //
[3] => /
[4] => This
[5] => is
[6] => a
)
[1] => Array
(
[0] => Это
[1] => текст
[2] => /
[3] => /
[4] => This
[5] => is
[6] => a
)
[2] => Array
(
[0] =>
[1] =>
[2] => /
[3] =>
[4] =>
[5] =>
[6] =>
)
[3] => Array
(
[0] =>
[1] =>
[2] =>
[3] =>
[4] =>
[5] =>
[6] =>
)
)
Тот же шаблон, только в профиль.И тут осеняет :)Этап четвертыйЧто нам нужно? Нам нужна последовательность символов, не являющаяся разделителями слов. Так? То есть, символы, не являющиеся всякими {=+ пробелами и проч.$pattern = '/([^\x20-\x2F|^\x3A-\x40|^\x5B-\x60|^\x7B-\x7F|^\s|^\n|^\r|^\f]+)/';
preg_match_all($pattern, $input, $arr);
Array
(
[0] => Array
(
[0] => Это
[1] => текст
[2] => This
[3] => is
[4] => a
[5] => text
)
[1] => Array
(
[0] => Это
[1] => текст
[2] => This
[3] => is
[4] => a
[5] => text
)
)
Ура! Так вот ты какой, северный олень. А казалось бы. Что нужно для счастья? Всего лишь нативная поддержка Юникода/многобайтных кодировок. Эххх.
ТЗ
http://kitya.livejournal.com/204801.html?thread=22178049#t22178049Если пользователи в ТЗ написали "геморрой", то программисты должны его им качественно обеспечить... :)
jQuery
jQuery - хорошая вещь. Маленькая да удаленькая.Например, у нас есть некоторое количество, скажем, статей с заголовками. Нам хочется, чтобы изначально тела статей были скрыты, а по клику на заголовок они раскрывались. А, ну да, и чтобы все это индексировалось... И чтобы четные и нечетные заголовки разными цветами были...Предположим, у нас такая структура документа:
<div id="wrapper">
<div>
<div id="title">Title 1</div>
<div id="body">Body 1</div>
</div>
<br /><br />
<div>
<div id="title">Title 2</div>
<div id="body">Body 2</div>
</div>
<br /><br />
<div>
<div id="title">Title 2</div>
<div id="body">Body 2</div>
</div>
</div>
Чтобы такое извратить в Яваскрипте, чтобы все работало по описанному выше сценарию? Не знаю, например, вот это:
<script>
$(document).ready(
function(){
$("#wrapper > div").each(
function(index){
var obj = $(this);
obj
.find("#title")
.css(
{
backgroundColor: index % 2 == 0 ? "#ef0000" : "#0000ef",
cursor: "hand",
cursor: "pointer",
color: "#FFF"
}
)
.click(
function(){
obj.find("#body").toggle();
}
)
.end()
.find("#body").hide();
}
)
}
);
</script>
:)Результат можно увидеть здесь: http://dmitriid.com/jquery. Как это все работает, можно будет увидеть в статье на РСДН, которую, я надеюсь, я скоро закончу :)Ах, да. Пример посмотртите и при выключенном Яваскрипте тоже.Мне jQuery нравится все больше и больше
Why's Poignant Guide по-русски
У себя в вики я начал перевод Why's Poignant Guide to Ruby. К сожалению, время у меня последнее время отсутствует, как класс (особенно для длительных сторонних проектов), а "Трогательный Путеводитель" перевести хочется.Поэтому присоединяйтесь!Перевод ведется в вики, доступно некоторое количество шаблонов для форматирования. В течение недели я, возможно, подкручу еще и syntax highlighting для Ruby Кросспост в ruby_ry, на RSDN и в RubyOnRails to russian
Erlang по-русски
Совсем забыл.На http://erlang.dmitriid.com ведется перевод документации по Эрлангу на русский язык. На данный момент переведены "Принципы дизайна OTP". Принимаются критика и предложения. Но не мне :) Я переводом не занимаюсь. Все вопросы напрямую в вики.Ура, товарищи! :)
Официальный сайт Orcas
Проект Orcas переместился на http://orcas.dmitriid.com/Описание проекта и прочая и прочая находятся в вики проекта по адресу http://orcas.dmitriid.com/wiki
Refactoring, pattern matching, function guards и Эрланг
Да, я помню, обещал рассказать что-нибудь про сопоставление с образцом (pattern matching), но не сегодня :) Хотя сегодняшний пост с ним, хоть и отдаленно, связан.Вратце, что такое сопосталение с образцом? Согласно всезнающей Википедии сопоставление с образцом - ничто иное, как способ проверить наличие составляющих в каком-либо образце/шаблоне в случае, если образец жестко задан Что это значит по-русски? Это значит, что если какая-то функция (особенно в динамически типизированных языках) способна принимать параметры совершенно разнообразных видов, то мы можем записать такую функцию примерно так:
fun([H|T]) ->
do_smth();
fun({a, tuple}) ->
do_smth_else();
fun(value) ->
do_a_third_thing();
fun(DefaultValue) ->
do_default_thing().
Что здесь происходит? 1. Первая функция принимает в качестве параметра массив/список, состоящий из двух частей, головы (Head) и хвоста(Tail). То есть, если мы передадим в функцию [1, 2, 3, 4], то внутри функции нам автоматом будут доступны две переменные - H и T. В H будет цифра 1, а в Т - массив [2, 3, 4]2. Вторая функция принимает кортеж (tuple), состоящий из двух неизменяемых значений, "a" и "tuple"3. Третья принимает неизменяемое значение "value"4. Четвертая принимает на вход все, что не приняли предыдущие функции То есть. Если мы вызываем
fun([2, 5, 72]).
то будет вызвана первая функция, потому что она принимает на вход массив/список.Если же мы вызываем
fun({another, tuple}).
то будет вызвана четвертая функция, потому что хотя третья и принимает кортеж, но она принимате только один кортеж - {a, tuple}, а мы передаем {another, tuple}.Так причем тут рефакторинг? И что за function guards?Два слова о function guards на примере набившей уже оскомину функции Фибоначчи. Сначала просто с сопоставлением с образцом:
fib(1) -> 1;
fib(2) -> 1;
fib(X) -> fib(X-1) + fib(X-2).
Теперь гарды:
fib(X) when X =< 3 -> 1;
fib(X) -> fib(X-1) + fib(X-2).
Первая строчка просто говорит: "Если Х меньше 3, то возвращаем 1". Гарды могут проверять огромное количество условий, включая таки, как is_list(X), is_tuple(X), и могут быть многоуровневыми, связываясь ключевыми словами and, or и так далее. Ну, о них потом, может быть, в других постах. Возвращаемся к теме нашего топика.На RSDN был задан вопрос о том, как реализовать шаблон Фабрика. Ну, например, для создания обертки над подключениям к разным базам данных. В качестве примера был приведен код на С++:
class Driver
{
public:
virtual void connect() = 0;
};
class MysqlDriver : public Driver {}; //implements connect() using some mysql API calls.
class OracleDriver : public Driver {}; //implements connect() using some oracle API calls.
И, наконец,
class DriverFactory
{
public:
static Driver * createDriver( string name ) //creates necessary driver
{
Driver * driver = 0;
if("mysql" == name ) driver = new MysqlDriver();
else if( "oracle" == name ) driver = OracleDriver();
else throw 1;
return driver;
}
};
Ключевыми, конечно, являются следующие строчки:
if("mysql" == name ) driver = new MysqlDriver();
else if( "oracle" == name ) driver = OracleDriver();
else throw 1;
Ну что ж. Перепишем все на Эрланге.Первое, что заядлому императивщику приходит в голову, это переписать пример, сохраняя его структуру:
start(Driver) ->
case Driver of
mysql ->
% совершаем коннект
{};
mnesia ->
% совершаем коннект
{};
odbc ->
% совершаем коннект
{}
end.
В общем, понятно, функцию start можно вызывать так:
start(mysql).
start(mnesia).
start(odbc).
Заметим, что конструкция case в Эрланге тоже использует сопоставление с образцом.Но что, если совершение подсоединения занимает довольно много места? Ну, скажем, 10-20 строчек кода? Тут мы можем вспомнить о гардах:
start(Driver) when Driver == mysql ->
% совершаем коннект
{};
start(Driver) when Driver == mnesia ->
% совершаем коннект
{};
start(Driver) when Driver == odbc ->
% совершаем коннект
{}.
Гарды и сопоставление с образцом в гардах. Уже интереснее, да и вызов функции не изменился, то все тот же start(имя_драйвера). Но вот гарды все же не слишком выразительны. Надо сначало прочитать объявление функции, а потом только глаз натыкается на условие. Можно ли это как-нибудь "облагородить"? Легко!Вспомниаем, что "сопоставление с образцом - это сопоставление с жестко заданным образцом". А что может быть более жесткой задачей, чем:
start(mysql) ->
% совершаем коннект
{};
start(mnesia) ->
% совершаем коннект
{};
start(odbc) ->
% совершаем коннект
{};
Этот пример можно слегка расширить, чтобы добавить возможность указания опций:
start(Driver) ->
start(Driver, []).
start(mysql, Options) ->
% совершаем коннект
{};
start(mnesia, Options) ->
% совершаем коннект
{};
start(odbc, Options) ->
% совершаем коннект
{};
start(_, _) ->
{error, driver_not_supported}.
Последняя строчка просто обозначает, что на все остальные варианты вызова мы выкидываем ошибку. Знак подчеркивания обозначает "любое значение", своеобразный "джокер".В этом, в целом состоит, как мне кажется, "Erlang Way" - способ подхода к решению проблем "по-эрланговски". Совсем коротко и не совсем точно можно сформулировать это так: сфокусируйся только на том, что тебе надо. Более расширенно, но тоже не совсем точно, это можно выразить в идее "агрессивного программирования". См. описание на английском на сайте Эрланга. То есть - программа/модуль/функция должна выполнять только то, для чего она предназначена. Во всех остальных случаях она должна просто упасть (естественно, сообщив, почему), потому что тот, кто неправильно этой программой пользуется, кто неправильно использует модули и функции внутри программы - сам себе злобный буратино. В итоге большую часть функций/модулей можно свести к компактному набору, который выполняет четко поставленные пред ним задачи и - не более. Что действительно удобно.Там же на странице описаны и другие детали "Erlang way", в частности: - программирование "сверху вниз" (от абстракций к деталям), - "сделай, чтоб работало, об оптимизации можно побеспокоиться потом", - принцип наименьшего удивления (система должна всегда отвечать предсказуемо)- "агрессивное" программирование и проч. Кстати, такие же подходы могут применяться - и применяются! - и в других, "мейнстримных" языках (те же С++, Java, C#), но для того, чтобы их придерживаться, нужен опыт, зачастую большой (камень в огород С++). Эрланг же подталкивает к такому подходу сразу.В общем, с Эрланга я уже не слезу :) Разве что на Nemerle :))
erlsoap
Мдя. Чем только не приходится заниматься :) Благодаря невероятному стечению обстоятельств некто Антон Федоров решил доразвить erlsoap, о котором я говорил в предыдущем сообщении. Началось сражение с новой, 0.4, версией erlsoap, которая наотрез отказывалась работать с сервисом RSDN. После полодотворного обмена сообщениями с Антоном буквально на следующий день появилась версия 0.4.1, которая не устроила Курилку :)) Дело в том, что erlsoap использует поставляемый с Erlang'ом модуль inets, который не поддерживает аутентификацию на прокси.Что поделать? Пришлось брать в руки шашку и идти рубать. То есть, добавлять поддержку ibrowse, который является более совершенным http клиентом, но пока не входит в официальную поставку Эрланга. В итоге - я впервые официально пропатчил opensource проект и, более того, даже упомянут внутри soapclient.erl в erlsoap версии 0.4.2.Ура, товарищи :)
Полку прибывает
Комрад Курилка проявил интерес к Касаткам. Пока что мы немного ломаем голову над тем, с какой стороны лучше подойти к SOAP.Так как запросов на РСДН не так уж и много, можно, теоретически, их просто выписать все ручками. Но ведь это неинтересно и скучно, ведь так? :)Взял я сегодня шашку наперевес и начал сражаться с erlsoap, который никто не развивает уже два года. Результаты можно увидеть в репозитории. Файл test_soap.erl, запускается через test_soap:run(). Курилка высказал вполне здравую мысль о том, что можно использовать Smerl. Но это так или иначе грозит ручной реализацией много чего. В общем завтра, если будет время, посмотрим.
Orcas
Таки начал слегка работать над клиентом к РСДНу на ЭрлангеСобственно сам проект: OrcasОбъявление об этом на КЫВТе здесь
Generat în 1.189 secunde. Thumbnail Screenshots by Thumbshots

