LINUX.ORG.RU
ФорумTalks

lisp vs everything - следующая задача


0

0

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

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

Подобная же задача - для любых языков (меня больше всего интересуют python, ocaml, java, ruby, php). Для динамических языков можно пользоваться динамической рефлексией, а не обрабатывать исходник.

★★★★★

> Необходимо: - разработать формат файла, который может хранить такие данные (типа persistency) - написать программу, которая умеет читать такой файл или писать его. - автоматизировать этот труд. Предполагаем, что со временем будут добавляться новые типы структур. Конечно, можно писать новый код сериализации-десериализации вручную, тогда должно быть дано однозначное описание (для обезьяны), как писать этот код. Но лучше, если будет предоставлен автоматический генератор, который берёт файл .h и выдает на выходе код для чтения-записи.

XML? более того, через XSLT можно и не писать ничего "на C", достаточно написать соотв. схему, через которую напуская процессор на XML с описанием структур генерить уже живой код.

или же я не понял, что конкретно хочется :-?

// wbr

klalafuda ★☆☆
()


и вообще, почему в Talks? перенесите в Development. иначе сейчас тему моментально засрут всем, чем угодно но только не советами по делу :-/

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

>или же я не понял, что конкретно хочется :-?

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

anonymous
()
Ответ на: комментарий от vada

> На JAVA даже изобретать ничего не надо. Все уже сделано до нас.

А ещё можно генератор случайных чисел запустить в космос, поближе к радиации.

Эффект примерно сравнимый с созданием армии жабописцев.

Gharik
()

пишешь на перле генератор быдлокода в С/C++ и всё ок, я так много раз делал. Было даже когда перл хавал xml, выдавал код на C который будучи отокмпиленным жрал яваскрипт.

vahvarh ★★★
()
Ответ на: комментарий от anonymous

Можно под заголовком "lisp vs everything - следующая задача" - понемногу подкидывать задачки, и лоровцы, сами того не зная, потихоньку допишут hurd.

Byron
()

На Java и писать-то практически нечего. Как бы Serializable, ObjectOutputStream, ObjectInputStream и все такое...

Ian ★★
()
Ответ на: комментарий от Ian

Код в студию.
Насчёт безработного - это в десятку!
Спасибо, вы дали мне повод посмеяться над собой ))))
Ну я, правда, ещё не совсем безработный, но уже почти.

den73 ★★★★★
() автор топика
Ответ на: комментарий от Gharik

А вы пишите... пишите. :)

Труд сишников напоминает басню И.А.Крылова. Там слова такие есть: "Мартышка вздумала трудиться, взяла чурбан, и ну над ним возиться..."

Пишите, гхарик, пишите... :)

vada ★★★★★
()

Одному мне показалось, что задачка не столь на ц, сколько на умение пользоваться yacc?

anonymous
()

Persistent CLOS Objects. Позволяет хранить объекты произвольной сложности и прозрачно истанцировать хоть локально, хоть удаленно, а также кешировать в 64 битной памяти, для скорости. Используется тракзакционная модель с commit/rollback, and optimistic concurrency.

Sun-ch
()
Ответ на: комментарий от Sun-ch

Писец, ты прям репликант профессора, только в розовой кофточке и лосинах ядовито-зеленого цвета

anonymous
()

> меня больше всего интересуют ocaml

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

libyaml добавит читабельности в это дело, но я его пока не щюпал.

вообще да, забавно :)

Rastafarra ★★★★
()

>Конечно, можно писать новый код сериализации-десериализации вручную

import pickle

....

obj = testObject() f = open("dump", "w") pickle.dump(obj, f) f.close()

ну и наоборот соотв.

не?

h8 ★★★
()
Ответ на: комментарий от den73

питон это, только форматирование уехало

h8 ★★★
()
Ответ на: комментарий от anonymous

Так, пример на лиспе. 

; определяем типы структур (можно в отдельном файле, пофиг, 
; язык динамический
(defstruct s1 f1 f2)
(defstruct s2 f3 f5)

(defparameter *root* ; создаём структуру
  (make-s1 :f1 1 :f2 (make-s2 :f3 "А как Русские?")))

(progn ; аккуратно зацикливаем
 (setf (s2-f5 (s1-f2 *root*)) *root*) 
  nil) ; nil нужен, чтобы не заклиться при печати на экран

(with-open-file ; выводим на печать. 
  (ou
  "file.out" 
  :direction :output
  :if-does-not-exist :create
  :if-exists :supersede
  )
 (let ((*print-circle* t) ; з
      (*print-pretty* nil)
      (*print-readably* t)) 
  (print *root* ou)
  nil
 ) 
)

содержимое файла при этом - такое:
#1=#S(S1 :F1 1 :F2 #S(S2 :F3 "А как Русские?" :F5 #1#)) 

(with-open-file  ; читаем обратно и печатаем на экран
  (in "file.out")
  (let ((root-2 (read in)))
   (let ((*print-circle* t) ; з
         (*print-pretty* nil)
         (*print-readably* t)) 
    (print root-2)
    nil
   )
  )
)

Прошу такой же пример на Вашем языке. Будут примеры - 
можно будет посоревноваться, кто быстрее. 

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Не совсем понял, что делает пример на лиспе :) 
В моеи случае два класса, экземпляры которых создаются и заносятся 
в файл. и соотв. наоборот:

#!/usr/bin/env python

import pickle

class test1(object):
	def __init__(self, value):
		self.value1 = value * 10
		self.value2 = "Class test1"

	def __str__(self):
		return "%s. Value - %s" % (self.value2, self.value1)
		
class test2(object):
	def __init__(self, value):
		self.value = "Class test2, Value - %i" % (value)

	def __str__(self):
		return self.value
		
def write_file():
	f = open("dump", "w")
	dump = []
	for i in range(10):
		dump.append(test1(i))
		dump.append(test2(i))
	pickle.dump(dump, f)
	f.close()
	
def read_file():
	f = open("dump", "r")
	dump = pickle.load(f)
	f.close()
	
	for obj in dump:
		print obj
		
if __name__ == "__main__":
	write_file()
	read_file()

 

h8 ★★★
()
Ответ на: комментарий от den73

Так где парсинг/генерация кода на С? Я правильно понял, что требуется по заголовочному файлу сгенерировать исходник с персистентными методами?

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Нет, тут имеется в виду, что описание типов структур должно быть на самом целевом языке. В случае C парсить нужно, в случае некоторых других языков есть рефлексия и поэтому парсить не нужно. В целом, задача такая: есть структуры - нужна рефлексия.

den73 ★★★★★
() автор топика
Ответ на: комментарий от h8

Пример на лиспе делает примерно то же самое, но у него у стр-ры типа s1, живущей в переменной *root*, в поле f2 живёт структура типа s2, а у той структуры в поле f5 со временем поселяется первая структура. Т.е., получается циклический граф. Его нужно аккуратно обойти начиная от корня и сделать дамп всего, что нам попадается. Если ветки срослись, то стараемся не зациклиться.

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

Так что пока питон ещё не достиг цели.

den73 ★★★★★
() автор топика
Ответ на: комментарий от klalafuda

Так тоже можно. Код, как я уже сказал, в студию.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

>Так что пока питон ещё не достиг цели.

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

h8 ★★★
()
Ответ на: комментарий от h8

Не понял. А как при чтении мы поймём, что экземпляр 1 ссылался на объект 2, а не на объект 3? Как мы их идентифицируем? Каким образом записывается дамп? И если мы будем всё писать тупо перебирая экземпляры, то где гарантия, что мы считаем экземпляр, а то, на что он ссылается, ещё не считано? Что тогда будет?

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

>А как при чтении мы поймём, что экземпляр 1 ссылался на объект 2, а не на объект 3

Да вот так:

>``Pickling'' is the process whereby a Python object hierarchy is converted into a byte stream, and ``unpickling'' is the inverse operation, whereby a byte stream is converted back into an object hierarchy. Pickling (and unpickling) is alternatively known as ``serialization'', ``marshalling,''13.1 or ``flattening''

Восстанавливается полностью состояние объектов. Вот тебе пример со ссылками на экземпляры:

#!/usr/bin/env python

import pickle

class test(object):
	def __init__(self, value):
		self.ref = None
		self.value = value

	def __str__(self):
		return "value %i + ref value %i" % (self.value, self.ref.value)
		
def write_file():
	x = test(1)
	y = test(2)
	y.ref = x
	z = test(3)
	z.ref = y
	x.ref = z
	
	f = open("dump", "w")
	dump = [x, y, z]
	pickle.dump(dump, f)
	f.close()
	
def read_file():
	f = open("dump", "r")
	dump = pickle.load(f)
	f.close()
	
	for obj in dump:
		print obj
		
if __name__ == "__main__":
	write_file()
	read_file()



h8 ★★★
()
Ответ на: комментарий от h8

А зачем тогда писать объекты 2 и 3? Попробуй писать только один объект. Наверняка получится.

Ну, в общем, я не удивлён тому, что питон оказался единственным языком, на котором нашёлся отвёт. Всё же питон - это урезанный лисп.

Ещё какие-нибудь варианты будут?

den73 ★★★★★
() автор топика

Lua 5.x:
--[[
  Author: Julio Manuel Fernandez-Diaz with modifications from RiciLake and me
-- $DESCR
--   сериализовать значение или таблицу.
--   УМЕЕТ сериализовывать таблицы с перекрёстными ссылками.
-- $IN
--   any t
--     значение/таблица
--   str name
--     имя переменной или таблицы
--   str/nil indent
--     начальные отступы
--   bool dofuncdump
--     true: делать string.dump()
-- $OUT
--  $OR
--   nil
--  $OR
--   str
--    результат, который можно скормить loadstring()'у.
function strutil.SerializeEx (t, name, indent, dofuncdump)
  local cart = {}; -- a container
  local autoref = {}; -- for self references

  local function IsTableEmpty (t) return next(t) == nil; end;

  local function BasicSerialize (o)
    local to, so = type(o), tostring(o);
    if to == "function" then
      if dofuncdump then
        local fn = string.dump(o);
        return FormatStr("loadstring(%s)", fn:Quote());
      else
        local info = debug.getinfo(o, "S");
        -- info.name is nil because o is not a calling level
        if info.what == "C" then return (so..", C function"):Quote();
        else -- the information is defined through lines
          return (so..", defined in ("..info.linedefined.."-"..info.lastlinedefined..")"..info.source):Quote();
        end;
      end
    elseif to == "number" or to == "boolean" or o == nil then return so;
    else return so:Quote();
    end;
  end;

  local function AddToCart (value, name, indent, saved, field)
    indent = indent or "";
    saved = saved or {};
    field = field or name;
    cart[#cart+1] = indent;
    cart[#cart+1] = field;
    if type(value) ~= "table" then
      cart[#cart+1] = "=";
      cart[#cart+1] = BasicSerialize(value);
      cart[#cart+1] = ";\n";
    else
      if saved[value] then
        cart[#cart] = "--";
        cart[#cart+1] = field;
        cart[#cart+1] = "=";
        cart[#cart+1] = saved[value];
        cart[#cart+1] = "\n";
        autoref[#autoref+1] = name;
        autoref[#autoref+1] = "=";
        autoref[#autoref+1] = saved[value];
        autoref[#autoref+1] = ";\n";
      else
        saved[value] = name;
        if IsTableEmpty(value) then cart[#cart+1] = "={};\n";
        else
          cart[#cart+1] = "={\n";
          for k, v in pairs(value) do
            k = BasicSerialize(k);
            local fname = FormatStr("%s[%s]", name, k);
            field = FormatStr("[%s]", k);
            -- one space between levels
            AddToCart(v, fname, indent.." ", saved, field);
          end;
          cart[#cart+1] = indent;
          cart[#cart+1] = "};\n";
        end;
      end;
    end;
  end;

  name = name or "__unnamed__";
  if type(t) ~= "table" then
    return "do\n"..name.." = "..BasicSerialize(t)..";\nreturn "..name..";\nend;";
  end;
  cart[1] = "do\n";
  AddToCart(t, name, indent);
  return join(cart)..join(autoref).."\nreturn "..name..";\nend;";
end;

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

есличо: "таблица" в Lua -- это и массив, и структура, и класс, и объект, и ступа с бабой ягой.

зыж для любителей: Lua умеет closures и правильно обрабатывает tail calls. %-)

anonymous
()
Ответ на: комментарий от anonymous

добавить после второй строки ']]'.

вывод — это исходник на самой Lua.

anonymous
()
Ответ на: комментарий от den73

> Ещё какие-нибудь варианты будут?

я тоже думал, что ты хочешь кодогенерацию, а так... даже не смешно.

[ocaml]
TYPE_CONV_PATH "server"

open Sexplib;;
open Sexp;;
open Conv;;



type t = { item1 : int;
           item2 : string }
with sexp;;



let v = { item1 = 11;
          item2 = "qwe qwe" };;



let _ =
  print_endline (to_string (sexp_of_t v));;
[/ocaml]

обратно: let v = t_of_sexp (from_string (...)) ...

Rastafarra ★★★★
()
Ответ на: комментарий от Rastafarra

Lua не совсем понял. Покажите, пожалуйста, пример, x ссылается на y, y на z, z на x. Интересует содержимое файла, полученного Вашей программой. Я что-то не вижу, где в файл выводится уникальное число или строка, идентифицирующая объект. Хотя уже сейчас ясно, что по духу задача решена.

ocaml - задача пока что _не_ решена. Требование состоит в том, что объекты ссылаются друг на друга, а Вы пока что сохранили только один объект. А что, with sexp позволяет иметь для каждого объекта sexp представление? А если у нас тип был без sexp, мы можем к нему сбоку приделать sexp через наследование или как там оно у вас назвается?

Парсер и кодогенерация нужны будет для решения этой задачи на C, т.к. в С нет ничего, похожего на рефлексию. Хотя (я надеюсь), что и для С эта задача уже решена. Только вот знает ли здесь кто-нибудь, как она решена, владеет ли инструментами?

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

не вопрос.
программо (тут можно сильно упростить, оно под скорость заточено, ну да не суть важно пока):
local join = table.concat;
local type = type;
local tonumber, tostring = tonumber, tostring;
local FormatStr, DupStr = string.format, string.rep;
local pairs, ipairs = pairs, ipairs;
local floor = math.floor;
local Date = os.date;
local SPEC_CHARS = {
  ["\7"] = "\\a",
  ["\8"] = "\\b",
  ["\9"] = "\\t",
  ["\10"] = "\\n",
  ["\11"] = "\\v",
  ["\12"] = "\\f",
  ["\13"] = "\\r",
};
function string.Quote (s)
  return '"'..s:gsub('[%z\1-\31%\\%"\127]', function (ch)
    local c = SPEC_CHARS[ch];
    if c then return c;
    elseif ch < " " then return FormatStr("\\%03i", ch:byte());
    else return "\\"..ch;
    end;
  end)..'"';
end;
local function SerializeEx (t, name, indent)
  local cart = {}; -- a container
  local autoref = {}; -- for self references
  local function IsTableEmpty (t) return next(t) == nil; end;
  local function BasicSerialize (o)
    local to, so = type(o), tostring(o);
    if to == "function" then error("DON'T WANT!");
    elseif to == "number" or to == "boolean" or o == nil then return so;
    else return so:Quote();
    end;
  end;
  local function AddToCart (value, name, indent, saved, field)
    indent = indent or "";
    saved = saved or {};
    field = field or name;
    cart[#cart+1] = indent;
    cart[#cart+1] = field;
    if type(value) ~= "table" then
      cart[#cart+1] = "=";
      cart[#cart+1] = BasicSerialize(value);
      cart[#cart+1] = ";\n";
    else
      if saved[value] then
        cart[#cart] = "--";
        cart[#cart+1] = field;
        cart[#cart+1] = "=";
        cart[#cart+1] = saved[value];
        cart[#cart+1] = "\n";
        autoref[#autoref+1] = name;
        autoref[#autoref+1] = "=";
        autoref[#autoref+1] = saved[value];
        autoref[#autoref+1] = ";\n";
      else
        saved[value] = name;
        if IsTableEmpty(value) then cart[#cart+1] = "={};\n";
        else
          cart[#cart+1] = "={\n";
          for k, v in pairs(value) do
            k = BasicSerialize(k);
            local fname = FormatStr("%s[%s]", name, k);
            field = FormatStr("[%s]", k);
            -- one space between levels
            AddToCart(v, fname, indent.." ", saved, field);
          end;
          cart[#cart+1] = indent;
          cart[#cart+1] = "};\n";
        end;
      end;
    end;
  end;
  name = name or "__unnamed__";
  if type(t) ~= "table" then
    return "do\n"..name.." = "..BasicSerialize(t)..";\nreturn "..name..";\nend;";
  end;
  cart[1] = "do\n";
  AddToCart(t, name, indent);
  return join(cart)..join(autoref).."\nreturn "..name..";\nend;";
end;
-- это циклический список, натурально
local st0 = {
  type = "st0",
  field0 = "bwah!",
  field1 = 154,
  field2 = "kwah!"
};
local st1 = {
  type = "st1",
  field0 = "zooma!",
  field1 = 776,
  field2 = "0x29a",
  another_field = "moorky",
};
local st2 = {
  type = "st2",
  field0 = "zooma!",
  field1 = 154,
  field2 = "0x29a",
  this_field_too = "stoopid",
};
st0.next = st1;
st1.next = st2;
st2.next = st0;
local res = SerializeEx(st0);
print(res);

anonymous
()
Ответ на: комментарий от anonymous

-----------------------------------------
результат:
-----------------------------------------
do
__unnamed__={
 ["field1"]=154;
 ["type"]="st0";
 ["next"]={
  ["field1"]=776;
  ["type"]="st1";
  ["another_field"]="moorky";
  ["next"]={
   ["field1"]=154;
   ["type"]="st2";
   --["next"]=__unnamed__
   ["field2"]="0x29a";
   ["this_field_too"]="stoopid";
   ["field0"]="zooma!";
  };
  ["field2"]="0x29a";
  ["field0"]="zooma!";
 };
 ["field2"]="kwah!";
 ["field0"]="bwah!";
};
__unnamed__["next"]["next"]["next"]=__unnamed__;

return __unnamed__;
end;
-----------------------------------------
вот это страшное чудовище можно скормить назад интерпретатору и получить то, что создавали с таким трудом.

anonymous
()
Ответ на: комментарий от anonymous

Нет, чё-то тут опять не так. А если два поля ссылочных, то тогда может оказаться два __unnamed__. На самом деле, нужно раздавать идентификаторы типа __unnamed1__, __unnamed2__ и т.п. Ну в общем, понятно, алгоритм обхода графа - это несложно. Ладно, насчёт Lua я понял (хотя, может у кого ещё будут какие вопросы). Ждём следующих (особенно интересна почему-то Java).

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

>А если два поля ссылочных, то тогда может оказаться два __unnamed__

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

__unnamed__["next"]["next"]["next"]=__unnamed__

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

зыж точнее, не исчезает, потому что я забыл слово local дописать, но это не важно в данном случае. %-)

anonymous
()
Ответ на: комментарий от anonymous

вряд ли сильно изменится. тут же нет метакода, чистый reflection, от исходников независимый. даже эта может быть поопрятней, тут много мусора с записью выходной строки по кускам в таблицу и немножко leftovers, потому что я выдрал сие «по живому» из рабочего кода. bringing this expiriense to the excellense is left to the reader (простите уж мне мой китайский %-).

anonymous
()
Ответ на: комментарий от anonymous

нихао. Просто пример как раз на метапрограммирование, интересно на него посмотреть на MetaLua (я его толком не знаю, только начал обычный Lua осваивать).

anonymous
()
Ответ на: комментарий от anonymous

для начала можно наверно сделать кальку с лиспового варианта, но интересны специфические штуки Lua.

anonymous
()
Ответ на: комментарий от anonymous

> посмотри на __unnamed__["next"]["next"]["next"]=__unnamed__ справа тут тоже может быть куча косвенных адресаций.

Может быть, и да, но хорошо ли это? Кольцевой двухсвязный список из трёх элементов как ты представишь? __unnamed__["next"]["next"]["prev"]=__unnamed["next"]? Фактически это и есть способ назначения идентификаторов - идентификатором служит путь от корня. Ну и зачем это нужно, если проще явно назначить идентификаторы.

Ну и я тоже хотел бы видеть пример на MetaLua, раз есть такое средство.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

>Фактически это и есть способ назначения идентификаторов — идентификатором служит путь от корня. Ну и зачем это нужно, если проще явно назначить идентификаторы.

а почему бы и да? ну, вот так вот сделано оно. без разницы, в общем — глазами же не читать.

>Ну и я тоже хотел бы видеть пример на MetaLua, раз есть такое средство.

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

anonymous
()
Ответ на: комментарий от anonymous

ну какое тут метапрограммирование-то? обычный reflection.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.