Home

Undesigned unfriendly page

Recent Entries

You are viewing the most recent 21 entries.

17th August 2007

8:16pm: StringTemplate креатифчик
StringTemplate - это шаблонная библиотека для генерации структурированного текста. Имеет функциональные черты, любит чистоту, немножко ленив, но заставляет выносить логику в модель.

Отчёт тут:
http://rsdn.ru/Forum/Message.aspx?mid=2625206
Current Mood: creative

22nd May 2007

4:21pm: Fantasy general
Эхх ностальгия... Пытался скачать сабж с

http://free-game-downloads.mosw.com/

Чтобы оттуда скачать, нужно прочитать это

We're sorry but because of increasing service maintaining costs we can't offer free downloads to everyone without limitations. However if you place a link to our site on your website, blog, or some social media site (Digg, MySpace, Del.icio.us, Reddit, StumbleUpon etc.) thus helping promote our site, we'll grant you free immediate access to download your favorite game or another files. We hope this measure is sufferable for you - it takes you just a moment and it really saves our site. If you really can't afford it, then we are very sorry, but there is no other way if we want to keep our site up. Thank you.


и поступить самым остроумным способом :-))

UPD: Нет, самым остроумным способом поступают товарищи с fgd. Скачиваем даунлоадер (Касперский молчит), и с помощью него скачиваем игру.
Current Mood: nostalgic

1st February 2007

6:16pm: Тухлятина для дуракоф!
LaPerouse

Ай кросавчег, ай да молодец. Так даже Влад не жог:

Видите ли, те языки, о которых вы говорите (C++, Pascal) в представлении многих являются классическими процедурными языками, что нет ничего недопустимого в усилении этого термина, которое более предпочтительно по сравнению с введением нового термина – “функциональный язык”. Исходя из того, что сказано мной выше, едва ли справедливо выделять из процедурных группу функциональных языков, тут, скорее, нужно говорить о “функциональном подходе” и о языках, наиболее эффективно поддерживающих этот подход.

Все-таки основным критерием функционального подхода является то, о чем я говорил выше – отношение к хранению состояния. Вот он, постамент, на котором зиждется вся теория и практика функционального программирования, тот самый “пятый элемент”, придающий особый колорит этому стилю. Стоит только разрешить изменение переменных в любом “функциональном языке” и внешние ссылки на эти переменные (которое не нужно даже специальным образом вводить), и этот язык неуловимо для глаз превратится в стандартный процедурный язык. Все остальное – синтаксические конструкции, помогающие объявлять вложенные конструкции функций, встроенные в сам язык возможности для обработки списков и т п — все это для дураков, чтобы дать им почувствовать всю “революционность” и крутизну “новейшего” подхода. Да! Я утверждаю, что за роскошной вывеской прячется тухлятина, которой уже как минимум 50 лет. И готов это доказать любому, пусть и придется пожертвовать частью личного времени на восполнение пробелов в чужом образовании.


http://rsdn.ru/forum/Message.aspx?mid=2329984

Даже если и так, я согласен чувствовать себя полным дауном читая не то что протухшие, а наверное уже разложившиеся листы рассылки erlang и haskell cafe. ^_^
Current Mood: amused

17th December 2006

3:04am: О прибыльном говне
Я комплители дизаппоинтед. Мы тут обсуждаем эрланг с его девятью девятками, ищем методы снижения количества ошибок и как можно более раннего их обнаружения (классическая тема для флейма: static vs dynamic typing), тем самым предполагая неявно, что чем меньше ошибок - тем лучше. Но есть места, где это не нужно...

Нед слофф
Current Mood: creative

13th December 2006

9:33pm: Много слов про Эрланг
Сообщение, в котором я пытаюсь обрисовать, то что джавов и дотнетов много, а Эрланг весь такой один. Получилось очень большим.

http://rsdn.ru/Forum/Message.aspx?mid=2261047

В обсуждении уже есть кое-что новенькое:

К>Т.е. надо в рантайме перехреначить байткод. Мне кажется, что АСТ курочить полегче будет.
К>Т.е. у тебя тут получится, что будет выполняться не байткод, а уже какой-то перетранслированный.

Не, как раз для этого есть куча средств. Сделать чтобы ВСЕ методы у ВСЕХ библиотек перед началом работы вызывали scheduler это 2-3 строчки кода, плюс добавить библиотеку и изменить startup script. И вообще, байткод гораздо проще чем AST языка. Правда, конечно, stack trace будет весьма странный получатся, что осложнит отладку довольно сильно.

Я знал, что это возможно, но не знал, что для "всех методов у всех библиотек" достаточно 3-х строчек кода. Разбираюсь, сейчас гляжу в доку по инструментированию JVM. Вижу только горячий апгрейд методов, но магических 3-х строчек не вижу.

Попутно жду железобетонных доводов "в реальных задачах такие ужимки и прыжки не нужны, и обычные потоки рулят"... ;-)
Current Mood: creative

10th December 2006

11:19am: graphviz
(Пора возрвратить к жизни эту запылившуюся страничку)

Есть надежда, что в rsdn скоро можно будет вставлять в сообщения dot-нотацию. То есть то же самое, что и со смайликами, только для включений вроде таких:
[gv]
digraph
{
  1->2->4->6
  2->3->4->5->6 
  1->3->5->6->1 
}
[/gv]

То есть вместо этого текста сгенерируется картинка и вставится в сообщение (по крайней мере предполагается так).

http://rsdn.ru/Forum/Message.aspx?mid=2257051&only=1
Current Mood: creative
Current Music: pzycho bitch - sweet kiss

2nd September 2006

7:15pm: Functional Programming For The Rest Of Us
Мой перевод неплохой на мой взгляд статьи
http://www.defmacro.org/ramblings/fp.html

Данная статья достаточно кратко и вполне доступно, используя примеры на Java (!), знакомит читателя с базовыми понятиями функционального программирования.


Пожалуйте подт кадт ^_^ )
Current Mood: creative

15th May 2006

11:40am: Рекурсивный перебор k-подмножеств
(Уфф, эти срочные проекты вышибли из колеи больше чем на месяц).

Хочу наконец закончить эстетствовать с перебором k-элементных подмножеств. Задачу сформулирую в наиболее общем виде: дан список (чего угодно), сформировать список всех его k-элементных подсписков.

start() ->
	comb([], [a,b,c,d,e,f], 3).

% comb([], [a,b,c,d,e,f], 3) ->
%	comb([a], [b,c,d,e,f], 2) ++
%	comb([b], [c,d,e,f], 2) ++
%	comb([c], [d,e,f], 2) ++
%	comb([d], [e,f], 2).	
comb(B, E, 1) ->
	Cons = fun (X) -> B ++ [X] end,
	map(Cons, E);
comb(B, E, K) when K == length(E) ->
	[B ++ E];
comb(B, E, K) ->
	combr(B, E, K-1).

combr(_B, E, K) when K == length(E) ->
	[];
combr(B, [EH|ET], K) ->
	%io:format("~w; ~w; ~w~n", [B ++ [hd(E)], tl(E), K]),
	comb(B ++ [EH], ET, K) ++ combr(B, ET, K).

Здесь текущее множество формируется из начала B и некоторого куска длины K из списка-конца E. В граничных условиях (когда нужно перебрать все 1-элементные подмножества, или E-элементные подмножества из E) задача решается непосредственным перебором, а в общем случае - рекурсивно. Красивое, компактное, хоть и не самое эффективное решение.
2> comb:start().
[[a,b,c],
 [a,b,d],
 [a,b,e],
 [a,b,f],
 [a,c,d],
 [a,c,e],
 [a,c,f],
 [a,d,e],
 [a,d,f],
 [a,e,f],
 [b,c,d],
 [b,c,e],
 [b,c,f],
 [b,d,e],
 [b,d,f],
 [b,e,f],
 [c,d,e],
 [c,d,f],
 [c,e,f],
 [d,e,f]]

Пока всё на этом.
Current Mood: creative

31st March 2006

10:38am: Перебор k-элементных подмножеств n-элементного множества
Да, вот такая простая задача. Везде видел решения рекурсией, но в один прекрасный момент (был он где-то 4 года назад) наткнулся на супербыстрый вариант без рекурсии:

void enumerate(int k, int n)
{
  int i, j;                                           
  int p = k-1; // позиция увеличиваемого элемента                   
                                                               
  for( j=0; j<k; ++j )                                           
    a[j] = j;                                                  
  // к сожалению крайние случаи приходится рассматривать отдельно
  if( k==n || k==0 )                                             
  {                                                              
    return true;                                               
  }                                                              
                                                               
  while( p >= 0 )                                                
  {                                                              
    // используем первые k элементов a (это то самое подмножество) 
    use(a, k);
    // генерируем следующее множество                          
    if( a[k-1] == n-1 )                                        
    {                                                          
        if( --p %gt;= 0 )                                         
            for( i=k; --i>=p ;)                                
                a[i] = a[p]+i-p+1;                             
    }                                                          
    else                                                       
    {                                                          
        p = k-1;                                               
        a[p]++;                                                
    }                                                          
}                                                              

Идея очень проста: увеличиваем элемент с индексом p на единичку, пока он не станет больше n. Когда этот последний элемент станет больше p, мы отодвигаем p биже к началу и заполняем элементы с p-го до k-го последовательно.

Этот алгоритм я и попытался изобразить на Эрланге (единственное отличие, что множество пришлось выписывать наоборот, потому что инкрементировать голову намного легче, чем хвост):
enum(N, K) when N >= K ->
	enum(N, K, seq(K, 1, -1), 1).
	
enum(N, K, Ls, P) when hd(Ls) > N ->
	overflow(N, K, Ls, P);
enum(N, K, Ls, P) ->
	do_something(Ls),
	enum(N, K, [hd(Ls)+1 | tl(Ls)], 1).

overflow(N, K, Ls, P, R) ->
	case split(P, Ls) of
		{_, []} -> done;
		{_, [Lsp | Lstt]} -> enum(N, K,
			seq(Lsp + P + 1, Lsp + 1, -1) ++ Lstt, P + 1, R)
	end.

do_something(Ls) ->
	io:format("~w~n", [Ls]).

Выводит
4> test:enum(6,3).
[3,2,1]
[4,2,1]
[5,2,1]
[6,2,1]
[4,3,1]
[5,3,1]
[6,3,1]
[5,4,1]
[6,4,1]
[6,5,1]
[4,3,2]
[5,3,2]
[6,3,2]
[5,4,2]
[6,4,2]
[6,5,2]
[5,4,3]
[6,4,3]
[6,5,3]
[6,5,4]
done
Current Mood: creative

21st March 2006

2:49pm: Задача: нужно перебрать следующие последовательности, перебор зависит от параметров m и n, где каждый элемент может принимать значения не больше m, всего элементов может быть не больше n, и они невозрастают.

Первый вариант:
test(Ba, Bl, A) when length(A) < Bl ->
	io:fwrite("~p~n", [A]),
	test(Ba, Bl, next(Ba, A));
test(Ba, Bl, A) ->
	done.

next(Ba, A) ->
	{Alow, Ahigh} = lists:splitwith( fun (X) -> X >= Ba end, A),
	next(Ba, Alow, Ahigh).

next(Ba, Alow, []) ->
	lists:duplicate(length(Alow) + 1, 2);
next(Ba, Alow, [Ah | At]) ->
	lists:duplicate(length(Alow) + 1, Ah + 1) ++ At.

Недостаток в том, что это нифига не эффективно, ибо splitwith проезжает по списку при каждом апдейте списка. Плохо.

Фторой вариант: фторого варианта пока нет... :-)

Тем не менее работает:
52> tmp:test(4,4,[]).
[]
[2]
[3]
[4]
[2,2]
[3,2]
[4,2]
[3,3]
[4,3]
[4,4]
[2,2,2]
[3,2,2]
[4,2,2]
[3,3,2]
[4,3,2]
[4,4,2]
[3,3,3]
[4,3,3]
[4,4,3]
[4,4,4]
done
Current Mood: creative

13th March 2006

6:23pm: Ещё табличка для оформления сорцов
package Hello;

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface CoolInterface_Home extends EJBHome { 
    public CoolInterface_Remote create() throws RemoteException, CreateException; 
}


<table border=1 cellspacing=1 bgcolor="lightgrey"><tr><td><pre>
Здесь идёт супер-пупер код
</pre></td></tr></table>

Наверное на этом варианте и остановлюсь.
Current Mood: creative
10:43am: Строка для медитации
Я выпал в осадок (довольно давно, надо сказать). Когда растворился обратно и подумал, что ЭТО нужно записать.

Такая вот маленькая неприментная строчечка из J4C:
  
  y =. 6 6 6 1 0 1
  t - (i.~y) { t =. /: /: y
0 1 2 0 0 1

Массив-результат обладает следующим свойством: r[i] это количество элементов равных y[i] с индексами меньшими i. То есть y[1] == 6, и r[1]==1 означает, что до этой шестёрки есть ещё только одна.

Генри Рич, комментируя этот глагол, говорит очевидные вещи, а неочевидные - не говорит :-) Так что пришлось самому. В результате многочасового битья головой об стену размышления я понял это. Попытаюсь изложить.

t это порядковый номер соответствующего элемента в y, есть термин такой, "ординал", так вот: t[i] это ординал y[i]-го. Убедиться в этом достаточно просто, скажу только что эта задача в Этюдах не продержалась и одного дня.

Всё множество элементов в y делятся на классы эквивалентности, а именно y1 и y2 попадают в один класс тогда и только тогда, когда они равны.

t обладает следующим свойством: для каждого класса эквивалентности соответствующие ординалы начинаются с самого левого элемента и идут последовательно. Выделенное было ключом для моего понимания. Смотрите сами: если бы было
  
  /:/: 2 2 2
1 2 0

то это не противоречило бы определению глагола /:, однако вся конструкция бы рассыпалась.

(i.~y) это (y i. y). Важно то, что здесь классы эквивалентности помечаются отдельными числами:
  
  ]x =. (i.~y) 6 6 6 1 0 1
0 0 0 3 4 3

Отмечу свойства x: x[i] <= i (очевидно) и числа-метки есть индекс первого числа соответствующего класса эквивалентности (это следует просто из определения i.).

Вот теперь ключевой момент: пусть задан какой-нибудь класс эквивалентности I (множество индексов). Утверждения:

Для любого i1, i2 \in I => y[i1]==y[i2].
Есть минимальный индекс im, im \in I.
В t значения на этих индексах идут по порядку, t[im] самый маленький.
В x значения на этих индексах равны im: x[i]==im для всех i \in I.
Следовательно, z =. x { t будет z[i]==t[im] для всех i \in I.
Следовательно, разность r = t - z будет принимать последовательные значения r[i] = 0, 1, .. ,k на индексах i \in I.

Вот и всё. Спустя некоторое время это станет мегаочевидно, и врядли будет стоить столько слов, сколько я сейчас накатал. Но сейчас оно того стОит!

Тацитная форма
  
  ([: /: /:) - i.~ { [: /: /:
      +- [:      
  +---+- /:      
  |   +- /:      
  +- -           
--+   +- ~ --- i.
  |   +- {       
  +---+     +- [:
      +-----+- /:
            +- /:


Quiz: как избавиться от дублирования ([:/:/:)?
Current Mood: creative

18th January 2006

3:33pm: Про управление персоналом.
Не хотелось сюда постить околопрограммистские ссылки, но эта зацепила

К вопросу об эффективном управлении персоналом

(впервые увидел у kmmbvnr, респект!)
Current Mood: creative

16th January 2006

11:29am: Какая классная фракталина!
http://www.ewartshaw.co.uk/jwhat.html

 {&'#.' @ (2:<|) @ ((+*:)^:400 0:)  (18 %~ i:_20) j.~/ 28 %~ _59+i.75
...........................................................................
...........................................................................
...........................................................#...............
...........................................................................
........................................................#..................
.......................................................##..................
.....................................................######................
.....................................................######................
......................................................####.................
............................................#....##############............
............................................##.##################.###......
.............................................#######################.......
.........................................#.#########################.......
..........................................###########################......
........................................###############################....
...............................#........##############################.....
...........................########....################################....
..........................###########..###############################.....
.........................#############.##############################......
......................#..#############.##############################......
...################################################################........
......................#..#############.##############################......
.........................#############.##############################......
..........................###########..###############################.....
...........................########....################################....
...............................#........##############################.....
........................................###############################....
..........................................###########################......
.........................................#.#########################.......
.............................................#######################.......
............................................##.##################.###......
............................................#....##############............
......................................................####.................
.....................................................######................
.....................................................######................
.......................................................##..................
........................................................#..................
...........................................................................
...........................................................#...............
...........................................................................
...........................................................................


Надо будет как-нить это в пикселях отобразить. Красиво ведь, а!

UPD: Под линуксами выдаёт непонятно что! :crash: Нужно поковырять, что к чему...
Current Mood: creative

6th January 2006

10:07am: Эксперименты с форматированием
Ruby small piece of code

class Person
  attr_accessor :name, :age
  def initialize(name, age)
    @name = name
    @age  = age.to_i
  end
  def inspect
    "#@name (#@age)"
  end
end

p1 = Person.new('elmo', 4)
p2 = Person.new('zoe', 7)

p1               # -> elmo (4)
p2               # -> zoe (7)


или можно так

  
class Person
  attr_accessor :name, :age
  def initialize(name, age)
    @name = name
    @age  = age.to_i
  end
  def inspect
    "#@name (#@age)"
  end
end

p1 = Person.new('elmo', 4)
p2 = Person.new('zoe', 7)

p1               # -> elmo (4)
p2               # -> zoe (7)


второй кусок наверное симпатичнее, ы?
<table><tr><td bgcolor="gray">&nbsp;</td><td>&nbsp;</td><td><pre>
тут идёт кусок кода
</pre></td></tr></table>

Интересно, какому гению в какое место пришёл этот дурацкий синтаксис html: &lt;?
Current Mood: creative

4th January 2006

4:45pm: Вы думаете я буду про Новый Год? Щазззззз
А я про J.

Некоторое время я пребывал в состоянии блаженной уверенности, что любой глагол записанный в явной форме можно записать в неявной. Нужно только немножечко хорошенько подумать. Но оказывается, всё чуть-чуть сложнее:
   t =. 4 : '0 (x.) } y.'
   2 t i.5
0 1 0 3 4

О сколько нам открытий чудных... (А.С.Пушкин)
Current Mood: creative

28th December 2005

2:48pm: Reverse Engeneering just for fun.

Эту штуку я запнул на rsdn в "Декларативное программирование", но для полноты картины сюда тоже решил. Взять запостить в список рассылки erlang-questions, что ли. Может быть аффтару, Urban Boquist-у будет приятно...

Итак, что мы видим сначала? Большую буковку "ё".

                             -module(e).
                         -export([main/1]).
                       -import(lists,[all/2]).
                         -define(t(A,B),B
                            andalso A).

f(E,R,L,A,N,G) ->

         if E>G->{c,N};L>G->f(E+1,R,R,R,N,G);A>G->c(
       R,N,G);true->case g(E,L,N)of g->f(E,R,L+1,R,N,G
     );_->b(G,E,A,R,N,L)end end. d(A,B)->h(3#21,B,A). h(
    A,B,C)->case f(C,             C,C,C,[{C-1,C-1,a}|B],A
   +2)of{c,D}->e(A+                 1,TL(lists:sort((D))));
 _->fail end. main                   (A)->d(1,A). b(E,R,L,A,
 N,G)when L>E->f(                     R,A,G,L,N,E);b(L,A,N,G,
 E,R)->V=round(math:sqrt(L)),C=fun(I)->(I-1)div V*V end,D=fun
 ({_,_,Q})->N/=Q end,case?t(all(D,[{I,O,S}||{I,O,S}<-E,A==I]),
 ?t(all(D,[{I,S,O}||{I,S,O}<-E,R==S]),all(D,lists:filter(fun({
 O,S,_})->?t(C(A)
 <O,?t(O=<C(A)+V,
 ?t(C(R)<S,S=<C(R
 )+V)))end,E))))of                                true
  ->f(A,G,R+1,G,[{A                              ,R,N}|E
   ],L);_->b(L,A,N+1,                           G,E,R)end.
    a(D,{C,A})->[[B||                          {_,_,B}<-C]|
      e(D-1,A)]. e(_,[                        ])->[];e(E,L)
        ->a(E+1,lists:split(E+1,L)). c(_,[{_,_,a}|_],_)->b
         ;c(B,[{R,K,N}|S],A)->f(R,B,K,N+1,S,A);c(_,[],_)
           ->b. g(B,A,[{B,A,_}|_])->g;g(_,_,[])->h;g(B
                ,A,[_|C])->g(B,A,C).
Прекрасно. Ломаем её отступами:
-module(e).
-export([main/1]).
-import(lists, [all/2]).

-define(t(A, B), B andalso A).

f(E, R, L, A, N, G) ->
    if 
        E > G -> {c, N};
        L > G -> f(E+1, R, R, R, N, G);
        A > G -> c(R, N, G);
        true -> case g(E, L, N) of
            g -> f(E, R, L+1, R, N, G);
            _ -> b(G, E, A, R, N, L)
        end
    end.

d(A, B) -> h(3#21, B, A).

h(A, B, C) ->
    case f(C, C, C, C, [{C - 1, C - 1, a} | B], A + 2) of
        {c, D} -> e(A + 1, tl(lists:sort((D))));
        _      -> fail
    end.

% A is list like [{1,3,8},{1,4,4},{1,7,3},{1,8,5},{2,6,1}, ... ].
main(A) -> d(1, A).

b(E, R, L, A, N, G) when L > E->
    f(R, A, G, L, N, E);
b(L, A, N, G, E, R) ->
    V = round(math:sqrt(L)),
    C = fun(I) -> (I - 1) div V * V end,
    D = fun({_, _, Q}) -> N /= Q end,
    case
        ?t(all(D, [{I, O, S} || {I, O, S} <- E, A == I]),
        ?t(all(D, [{I, S, O} || {I, S, O} <- E, R == S]),
        all(D, lists:filter(
            fun({O, S, _}) -> 
                ?t(C(A) < O, ?t(O =< C(A) + V, ?t(C(R) < S, S =< C(R) + V)))
            end, E))))
    of
        true -> f(A, G, R + 1, G, [{A, R, N} | E], L);
        _    -> b(L, A, N + 1, G, E, R)
    end.

a(D, {C, A}) -> [[B || {_, _, B} <- C] | e(D - 1, A)].

e(_, []) -> [];
e(E, L) -> a(E + 1, lists:split(E + 1, L)).

c(_, [{_, _, a} | _], _) -> b;
c(B, [{R, K, N} | S], A) -> f(R, B, K, N + 1, S, A);
c(_, [], _) -> b.

g(B, A, [{B, A, _} | _]) -> g;
g(_, _, []) -> h;
g(B, A, [_|C]) -> g(B, A, C).
Итак, расставили отступы... Дааа, жуть. Ищем main. Спасительной информацией для понимания является то, что main принимает в качестве параметра список троек {Строка, Столбец, Номер}. Функция main на редкость простая. Смотрим дальше: ага, там функция d просто вызывает h. Двигаемся дальше, к определению функции h. Видим, что к первому параметру A прибавляются 1 и 2, но функция h больше нигде не вызывается. Это даёт нам право немного модифицировать вызов функции h и её саму:
main(List) -> h(7 + 2, List, 1).

h(Size, List, I) ->
    case f(I, I, I, I, [{I - 1, I - 1, a} | List], Size) of
        {c, D} -> e(Size - 1, tl(lists:sort((D))));
        _      -> fail
    end.

(3#21 это просто число 7, такой вот способ записи, явно для запудривания мозгов).

Далее рассматриваем мешанину символов... Вдруг - о! - что-то знакомое: функция g - это явно поиск пары в списке. Эта функция вызывается только из f. Первые 2 параметра у g - это номера строки и столбца ячейки. Следовательно, список параметров f уточняется до:

f(Rw, R, Cl, A, L, Size) -> ...
Что такое R и A здесь - пока непонятно. Функцию g перепишем в более удобоваримом виде:
find_cell(Rw, Cl, [{Rw, Cl, _} | _]) -> found;
find_cell(_, _, []) -> not_found;
find_cell(Rw, Cl, [_|L]) -> find_cell(Rw, Cl, L).

Смотрим на параметр R у функции f. Он при первом вызове получает значение 1, затем появляется в функции b под именами A (первое "тело") и G (второе "тело") и в функции с под именем B, но при этом никогда не принимает нового значения! Переименовываем его в I (потому что I похожа на единичку) где только можно. Нужно только позаботиться об отсутствии коллизии имён в функции b.

Мешает макрос. Макрос в баню, тогда условие в case можно записать так (здесь я уже переименовал A->Rw и R->Cl исходя из соответствия функции f):

all(D, [{I, O, S} || {I, O, S} <- E, Rw == I] andalso
all(D, [{I, S, O} || {I, S, O} <- E, Cl == S] andalso
all(D, lists:filter(
    fun({O, S, _}) -> 
        C(Rw) < O andalso O =< C(Rw) + V andalso C(Cl) < S andalso S =< C(Cl) + V
    end, L))
(Чтобы не заставлять читателя лезть в доку: выражение [X || X <-List, P(X)] означает "все X из списка List, для которых предикат P(X) выдаёт true"). Возникает интересное подозрение... Посмотрим, что выдаёт C(X):
1> C=fun(X)->(X-1) div 3 * 3 end.
#Fun<erl_eval.6.43886099>
2> lists:map(C, lists:seq(1,9)).
[0,0,0,3,3,3,6,6,6]

Ага! А функция D проверяет, не равна ли ячейка числу N. Да, это оно! Это условие выражает основное правило Судоку: число в текущей ячейке не должно совпадать ни с одним из чисел в строке, ни в столбце и ни в соответствующем текущей ячейке квадрате.

Теперь _очень пристально_ смотрим на параметр A у функции f. Видно, что он используется очень активно и он попадает в функцию b (2 тело) под именем N, на котором и завязано условие. Всё ясно - это номер в текущей ячейке.

Далее, переименовываем параметры у функций b и c так, чтобы они соответствовали параметрам f. Получим (опуская несущественные пока детали):

f(Rw, I, Cl, Nm, L, Size) -> ....
b(Size, Rw, Nm, I, L, Cl) -> ....
...
c(I, [{Rw, Cl, Nm} | L], Size) -> f(Rw, I, Cl, Nm + 1, L, Size);
...

Итак, ещё раз: Rw, Cl - координаты текущей ячейки, Nm - текущий номер, L - список троек, Size - в данном случае всегда 9, I - в данном случае всегда 1.

Снова пристально смотрим на f, что же она делает:
1. Если текущая координата строки большая - то выдаёт решение;
2. Если текущая координата столбца большая - то переходит к следующей строке;
3. Если текущий номер в ячейке большой - то вызов функции c(...);
4. Ищет текущую ячейку в списке,
4.1 если нашлась - то переходит к следующей ячейке в текущей строке;
4.2 если не нашлась - то вызывает b с текущей ячейкой и номером == 1.

Пристально смотрим на b, что делает она:
0. Если текущий номер слишком большой, то вызывает f (это срабатывает тело с гвардом);
1. Для данного номера проверяет, можно ли вписать его в текущую ячейку,
1.1. если можно, то добавляет ячейку в список и вызывает f;
1.2. если нельзя, то вызывает себя со следующим номером.

Теперь по-русски. Функция f двигается по матрице справа налево, сверху вниз. Если в ячейке ещё пусто, то вызывается функция b. Функция b последовательно подбирает номер в текущей ячейке начиная с 1. Подбор заканчивается либо добавлением новой ячейки в список, либо передачей в f параметра Nm == 10. В последнем случае срабатывает условие:

    Nm > Size -> c(I, L, Size);

Теперь понятно, что делает функция c. Функция c - это откат. Если головоломка не совсем банальная, то однажды процесс заткнётся тем, что в текущую ячейку невозможно будет вписать никакой номер от 1 до 9. В этом случае происходит откат: от списка "откусывается" головной элемент, и подбор продолжается со следующего номера.

Остаются мелочи - функции a и e. e - это просто преобразование списка троек в матрицу. Список троек должен быть полным, иначе функция завалится. Функция a "откусывает" 9 троек и вытягивает их в линейный список. Можно избавиться от E+1, заменив на просто E, но тогда соответственно поменять и вызов в функции h.

Долго ли, коротко ли, но окончательный вариант этого рефакторинга превратился в:

-module(e).
-compile(export_all).
%-export([main/1]).

% (1..10), 1, (1..10), (1..10), List, 9
set_cell(Rw, I, Cl, Nm, L, Size) ->
    if 
        Rw > Size -> {done, L};
        Cl > Size -> set_cell(Rw+1, I, I, I, L, Size);
        Nm > Size -> rollback(I, L, Size);
        true -> case find_cell(Rw, Cl, L) of
            found -> set_cell(Rw,   I, Cl+1, I, L, Size);
            _     -> try_number(Size, Rw, Nm, I, L, Cl)
        end
    end.

% 9, List, 1 
start(Size, List, I) ->
    case set_cell(I, I, I, I, [{I - 1, I - 1, a} | List], Size) of
        {done, L} ->
            io:format("~w~n", [lists:sort(L)]), % отсебятина :-)
            list2matr(Size, tl(lists:sort((L))));
         _        -> fail
    end.

% List is list like [{1,3,8},{1,4,4},{1,7,3},{1,8,5},{2,6,1}, ... ].
main(List) -> start(9, List, 1).

try_number(Size, Rw, Nm, I, L, Cl) when Nm > Size ->
    set_cell(Rw, I, Cl, Nm, L, Size);
try_number(Size, Rw, Nm, I, L, Cl) ->
    V = round(math:sqrt(Size)),
    Cvv = fun(X) -> (X - 1) div V * V end,
    Neq = fun({_, _, X}) -> Nm /= X end,
    case
        lists:all(Neq, [{R, C, N} || {R, C, N} <- L, Rw == R]) andalso 
        lists:all(Neq, [{R, C, N} || {R, C, N} <- L, Cl == C]) andalso
        lists:all(Neq, lists:filter(
            fun({R, C, _}) -> 
                Cvv(Rw) < R andalso R =< Cvv(Rw) + V andalso 
                Cvv(Cl) < C andalso C =< Cvv(Cl) + V
            end, L))
    of
        true -> set_cell(Rw, I, Cl + 1, I, [{Rw, Cl, Nm} | L], Size);
        _    -> try_number(Size, Rw, Nm + 1, I, L, Cl)
    end.

% L1, L2 - lists, E - integer
extr_row(E, {L1, L2}) -> [[N || {_, _, N} <- L1] | list2matr(E, L2)].

% E - integer, L - list
list2matr(_, []) -> [];
list2matr(E, L) -> extr_row(E, lists:split(E, L)).

% 1, List, 9 
rollback(_, [{_, _, a} | _], _) -> fail;
rollback(I, [{Rw, Cl, Nm} | L], Size) -> set_cell(Rw, I, Cl, Nm + 1, L, Size);
rollback(_, [], _) -> fail.

% (1..9), (1..9), List
find_cell(Rw, Cl, [{Rw, Cl, _} | _]) -> found;
find_cell(_, _, []) -> not_found;
find_cell(Rw, Cl, [_|L]) -> find_cell(Rw, Cl, L).
Пара замечаний:

1. Поиск здесь в глубину с возвратом, однако возврат выражается только в удалении новых троек из (текущего) списка. Сами вызовы в течение этого поиска "забираются" всё глубже и глубже в рекурсию. Тем не менее с памятью вообще и стеком в частности всё в порядке, потому что железно работает last call optimization. Слегка непривычно, совсем не так как в С??, где для отката нужно было бы выходить из текущего уровня рекурсии. (Но там и таскать с собой текущий список в качестве параметра тоже не нужно было бы). Хотя есть и альтернативные варианты: сделать так, чтобы функции set_cell и try_number возвращали пару {List, result}, или принимали List, а возвращали result. Тогда глубина рекурсии должна существенно уменьшиться.

2. Даже обфусцированные функциональные программы по-прежнему доступны для понимания, поскольку результат работы функции целиком зависит от параметров и можно на мгновение забыть вообще обо всей программе сконцентрировавшись только на этой функции. Также сильно облегчает анализ неизменность "забинженных" переменных - то бишь прозрачность ссылок (внутри функции куда ни ткни - везде Nm это Nm). Трудности пожалуй могут возникнуть в случаях"большой-пребольшой функции у которой много-премного параметров" и "много-премного переплетённых (взаимно-рекурсивных) функций у которых много-премного параметров".

Собстно вот, ду хаст!

Current Mood: creative

26th December 2005

12:43pm: Маленькое усовершенствование для "домика Пи"
Как и раньше
   pitriang =. ([:#~odd) ,/. pilist
   pitriang 5
3
141
59265
3589793
238462643
   u =. (- @ |."1 @ i.)
   pitree = u |."0 1 pitriang
   pitree 5
    3
   141
  59265
 3589793
238462643

Такой вот юз глагола "|." и рангов можно сказать ради юза. Напоследок ещё пара замечаний:
   u 5
_4 _3 _2 _1 0
   -i.- 5
_4 _3 _2 _1 0

Конечно, второй вариант симпатичнее. Собственно, написал я первый вариант только ради того, чтобы поупражняться с рангами и "|.". Упражнение понравилось :-)
Current Mood: creative

20th December 2005

8:50am: Новые, неизведанные глубины в J
Читал вот статью "Functional programming and the J programming language". Какую однака штуку я обнаружил:
   pprod =: +/ /. @ (*/)
   1 2 pprod 1 3 1
1 5 7 2


Да. Всего-навсего. Это перемножение полиномов, заданных вектором коэффициентов. И что? Дальше интереснее. fndisplay выдаёт очевидно
   x (P Q@R) y
+-------------+
|x _P_ Q_ R_ y|
+-------------+
   NB. в данном случае (+/ это P, /. это Q, */ это R

Если мы это дело перепишем в виде (по-честному, со скобочками), получим линейкой по рукам:
   1 2 +/ (/. (*/) 1 3 1)
|syntax error
|   1 2+/(    /.(*/)1 3 1)


Коробочный вариант однако немного проясняет ситуацию:
   pprod
+----------+-+-----+
|+-----+--+|@|+-+-+|
||+-+-+|/.|| ||*|/||
|||+|/||  || |+-+-+|
||+-+-+|  || |     |
|+-----+--+| |     |
+----------+-+-----+

Из этой картинки следует, что у нас вилка, согласно определению вилки
   x (P Q R) y
(x _P_ y) _Q_ (x _R_ y)


x */ y даёт табличку, затем применяется (+/ /.) к этой табличке, а не к исходным аргументам (почему? возможно из-за @). Ну и полученный вектор выводится на экран.

Короче, парсинг строк с причастиями - это какой-то магический кристалл. Неужели предсказать поведение луча света в нём могут только маги? Где тот тайный манускрипт, который бы подробно и обстоятельно раскрыл механизм парсинга выражения J?

fndisplay конечно хорошо, но мало.
Current Mood: creative

16th December 2005

5:23pm: Пи домиком
Какую я красивую штуку нашёл, ну просто...

   pi=: [: ": [: <.@o. 10x"_^]
   pi_list=: [: pi [: <: [: *: ]
   pi_triangle=: ([: #~ odd) ,/. pi_list
   odd=: 1: + 2: * i.
   pi_tree=: ([: - [: i. -@]) |."0 1 pi_triangle
   pi_tree 16
               3               
              141              
             59265             
            3589793            
           238462643           
          38327950288          
         4197169399375         
        105820974944592        
       30781640628620899       
      8628034825342117067      
     982148086513282306647     
    09384460955058223172535    
   9408128481117450284102701   
  938521105559644622948954930  
 38196442881097566593344612847 
5648233786783165271201909145648


Ещё хорошая новость есть - код выше уже не выглядит простым набором символов, и я не впадаю в жёсткий тупняк, когда вижу такое. А даже наоборот, лишь некоторые места непонятны. :super:

Последняя хорошая новость. Выложил на RSDN наконец то, что давно хотел выложить, да времени всё не было. Take a look and enjoy.

ps: нужно фак читать по форматированию...
Current Mood: creative

14th December 2005

12:03pm: Разбираюсь в интерфейсе LJ. Смешно, но тыкаюсь как слепой котёнок. И всё вроде бы легко, и многое непонятно. Нужно будет ещё повспоминать интересы (:-!) и настроить стартовую страницу, а то убого софсем...
Current Mood: creative
Powered by LiveJournal.com