LINUX.ORG.RU
Ответ на: комментарий от eugine_kosenko

Фиксед. Решил запостить сюда. Теперь работает на любой 
sqlalchemy 0.2.x

Надо было исправить sqlalchemy.objectstore.get_session().flush() на
sqlalchemy.objectstore.flush()

Что-то я протупил. 
Не знаю с какого ляда оно работало на более ранних версиях :-)

#!/usr/bin/env python

import sys
import os.path

import sqlalchemy.mods.threadlocal
import sqlalchemy
import sqlalchemy.exceptions

class File(object):
    def __init__(self, **kwargs):
        for key, value in kwargs.iteritems():
            setattr(self, key, value)

    def __unicode__(self):
        s = [ unicode(self.__class__.__name__) + u': ' ]
        for c in self.c:
            s.append(u'%s=%s ' % (c.key, getattr(self, c.key)))
        result = u''.join(s)
        return result
    
metadata = sqlalchemy.BoundMetaData('sqlite:///files.db',
    strategy='threadlocal')

files_table = sqlalchemy.Table('files', metadata,
    sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True),
    sqlalchemy.Column('name', sqlalchemy.String(1000), index=True),
    sqlalchemy.Column('size', sqlalchemy.Integer, index=True),
    sqlalchemy.Column('dir', sqlalchemy.String(1000), index=True),
    sqlalchemy.Column('count', sqlalchemy.Integer, index=True))

sqlalchemy.mapper(File, files_table)

class FileDb:
    def __init__(self):
        try:
            sqlalchemy.Query(File).select(limit=1)
        except sqlalchemy.exceptions.SQLError:
            metadata.create_all()
    
    def flush(self):
        sqlalchemy.objectstore.flush()
    
    def add(self, full_name, size):
        dir, name = os.path.split(full_name)
        count = self.updateFile(name, size)
        File(name=name, dir=dir, size=size, count=count)
        self.flush()
    
    def delete(self, full_name, size):
        dir, name = os.path.split(full_name)
        count = self.updateFile(name, size, -1)
        [sqlalchemy.objectstore.delete(f) for f in 
            sqlalchemy.Query(File).select_by(name=name, dir=dir, size=size)]
        self.flush()
    
    def updateFile(self, name, size, delta=1):
        files = list(sqlalchemy.Query(File).select_by(name=name, size=size))
        count = len(files)
        for f in files:
            f.count = count + delta
        return count + delta
    
    def select(self, condition=files_table.c.count >= 0):
        return sqlalchemy.Query(File).select(condition)
    
def main():
    filedb = FileDb()
    
    if 'add' in sys.argv:
        filedb.add('/home/vasily/pic/zombieeuiip.jpg', 31558)
        filedb.add('/home/vasily/zombieeuiip.jpg', 31558)
        
        filedb.add('/home/vasily/pic/sars.jpg', 19311)
        filedb.add('/home/vasily/sars.jpg', 19311)
        filedb.add('/home/vasily/junk/sars.jpg', 19311)
        
        filedb.add('/home/vasily/pic/pic6.jpg', 29638)
        filedb.add('/home/vasily/pic/pic8.jpg', 17577)
    
    if 'delete' in sys.argv:
        filedb.delete('/home/vasily/pic/zombieeuiip.jpg', 31558)
        
    if 'select' in sys.argv:
        print 'All files:'
        for f in filedb.select():
            print u'%s' % f
            
        print
        print 'N > 2'
        for f in filedb.select(files_table.c.count > 2):
            print u'%s' % f
        
        print
        print 'N != 2'
        for f in filedb.select(files_table.c.count != 2):
            print u'%s' % f
        
        print
        print 'N < 3'
        for f in filedb.select(files_table.c.count < 3):
            print u'%s' % f
        
if __name__ == '__main__':
    main()

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

>Согласен. В lisp'е как раз не хватает порой гибкости stl. В lisp'е 
если я определил новый тип-контейнер, то мне придется писать все эти 
map, reduce, remove, ... с нуля. И имена у них будут уже другими. Это 
минус.

Это не то? Стандартные макросы defgeneric/defmethod:

(defgeneric plus (obj1 obj2))

(defmethod plus ((obj1 integer) (obj2 integer)) (+ obj1 obj2))

(defmethod plus ((obj1 string) (obj2 string)) (concatenate 'string obj1 obj2))

(defmethod plus ((obj1 мой-тип-контейнер) (obj2 string)) (...павлиноуткоёж...))

*(plus 1 2)
3
*(plus "a" "df")
"adf"

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

> > В таком случае правильное решение: сделать абстрактный тип

> А зачем тебе _в_этом_ случяе абстрактный тип? Слелай конкретный тип - "супер-пупер-навороченое-граф-с-сверхбыстрым-удалением" и высокоуровневые операции с им: добавить узел, итд.

Дык я про то и говорю. Но для очевидных generic (в STL-ном смысле этого слова) операций (map, reduce, etc) мне придется писать свой код, практически полностью дублирующий стандартные map, reduce, etc. А в STL мне это не нужно было бы делать. Это минус. В плюсе то что очень полезную при обработаки графов операцию pattern matching я могу очень красиво интегрировать в lisp, а для C++ это если и возможно, то через очень черную магию template metaprogramming.

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

> Дык я про то и говорю. Но для очевидных generic (в STL-ном смысле этого слова) операций (map, reduce, etc) мне придется писать свой код, практически полностью дублирующий стандартные map, reduce, etc. А в STL мне это не нужно было бы делать. Это минус.

Т.е. если тебя не устраивают стандартные алгоритмы в лиспе, ты жалуесся чё тебе их нужно будет переписывать? А если мя не устроят STLные, их не нужно? Я прально понял?

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

> Нет никаких причин (кроме лени и бессмысленности самого процесса)...

Ок, оставьте лисп в покое :(

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

>Это не то? Стандартные макросы defgeneric/defmethod:

>(defgeneric plus (obj1 obj2))

>(defmethod plus ((obj1 integer) (obj2 integer)) (+ obj1 obj2))

>(defmethod plus ((obj1 string) (obj2 string)) (concatenate 'string obj1 obj2))

>(defmethod plus ((obj1 мой-тип-контейнер) (obj2 string)) (...павлиноуткоёж...))

>*(plus 1 2)

>3

>*(plus "a" "df")

>"adf"

То. Но было, бы неплохо если бы #'+ (а не #'plus) был generic function. Мелочь конечно, но все равно минус. Именно это я и имел в виду выше по поводу невозможности перегрузки #'+.

Да, это можно исправить через (progn (shadow '+) (defgeneric + (first-arg &rest args))), но при этом, похоже, придется попотеть с define-compiler-macro, чтобы не потерять производительность.

И еще, т.к. мы определяем другую функцию и пользуем другой символ, то все в стандартной библиотеке, что пользует #'+ и что нам надо расширить придется править по такой же схеме. Пример: incf, decf.

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

> Т.е. если тебя не устраивают стандартные алгоритмы в лиспе, ты жалуесся чё тебе их нужно будет переписывать?

Меня устраивают стандартные алгоритмы. Я ворчу про то, что стандарт не позволяет мне наследоваться от sequence и применять эти алгоритмы для своих типов.

Можно глянуть, к примеру на то как это сделано в Ruby, где для своего типа мне достаточно определить метод each и включить mixin Enumerable, чтобы получить map, inject (примитивный аналог reduce) и др. полезные методы. Ruby'евский подход, правда, создает большие проблемы для эффективной оптимизации этих generic методов. Но, ИМХО, эти проблемы все таки преодолимые.

BTW, насколько я помню идеи, стоящие за STL были сперва опробованы тов. Степановым на Scheme.

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

> Нафик надо-то, чтоб потом геморроиться с никакой интеграцией в unix?

Не, нафиг не надо. Пишите дальше всё на си и си++. А чего вас то сюда занесло? Боитесь, что мы без вашей правды пропадём? Или топик не читали? Или четали, но н поняли?.. :)

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

> В lisp'е как раз не хватает порой гибкости stl.

Я что-то пропустил?

> В lisp'е если я определил новый тип-контейнер, то мне придется писать все эти map, reduce, remove, ... с нуля. И имена у них будут уже другими. Это минус.

Переопредели, потому как...

> Также в лиспе нельзя доопределить новый численный тип и перегрузить #'+, #'*, ... для него. К примеру, если мне нужны кватернионы, то складывать их придется уже не с помощью #'+.

...это наглая ложь!

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

> Справедливости ради, это ("другие имена") можно излечить макросами, вроде бы.

Справедливости ради, переопределить можно _любую_ функцию.

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

> Меня устраивают стандартные алгоритмы. Я ворчу про то, что стандарт не позволяет мне наследоваться от sequence и применять эти алгоритмы для своих типов.

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

> Можно глянуть, к примеру на то как это сделано в Ruby, где для своего типа мне достаточно определить метод

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

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

>Справедливости ради, переопределить можно _любую_ функцию.

Не из пакета common-lisp. Стандарт явно говорит, что последствия попытки сделать это undefined. В SBCL пакет common-lisp специальным образом заблокирован. Блокировку можно снять. Но при этом отнюдь не факт, что переопределенный common-lisp:+ или common-lisp:map будут нормально работать, т.к. компилятору разрешено (стандартом) считать что #'common-lisp:+ это всегда именно та функция, что определена стандартом.

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

> > Меня устраивают стандартные алгоритмы. Я ворчу про то, что стандарт не позволяет мне наследоваться от sequence и применять эти алгоритмы для своих типов.

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

Не надо путать sequence (абстрактный класс, или интерфейс в терминологии Java) и конкретные типы вроде list и array.

В стандарте map, reduce, etc определяются именно в терминах sequence, но при этом они не generic функции, и их невозможно применять для других подклассов sequence. Это печально.

> > Можно глянуть, к примеру на то как это сделано в Ruby, где для своего типа мне достаточно определить метод

> В лиспе методы сугубо вторичны. Т.е. Ели желаеш юзать ооп, определяй обект с методами и наследуй ево в своё удовольствие. Получиш то же самое, что и в руби и где угодно, только не на уровне языка конешно, потому что применение ооп слишком редко оправдано, чтобы ради нево язык коверкать.

Да вторичны, но соотвествующие им generic функции не отличаются по статусу от всех остальных. То как map, reduce и иже с ними расширяются на used-defined типы в Ruby это лишь один из способов. Создатели common-lisp могли бы им последовать. Они могли бы найти и более lisp-way для этого. В любом случае, возможность применять эти функции для определяемых пользователем типов была-бы полезна.

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

> Не из пакета common-lisp. Стандарт явно говорит, что последствия попытки сделать это undefined. В SBCL пакет common-lisp специальным образом заблокирован. Блокировку можно снять. Но при этом отнюдь не факт, что переопределенный common-lisp:+ или common-lisp:map будут нормально работать, т.к. компилятору разрешено (стандартом) считать что #'common-lisp:+ это всегда именно та функция, что определена стандартом.

Что вам мешает переопределить _любые_ функции _только_ для своего пакета?

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

> В стандарте map, reduce, etc определяются именно в терминах sequence, но при этом они не generic функции, и их невозможно применять для других подклассов sequence. Это печально.

А с чего вы взяли, что sequence - класс? В лиспе классы и типы - совершенно разные "вещи". И их "взаимоотношение" стандартом практически не оговаривается.

> Да вторичны, но соотвествующие им generic функции не отличаются по статусу от всех остальных. То как map, reduce и иже с ними расширяются на used-defined типы в Ruby это лишь один из способов. Создатели common-lisp могли бы им последовать. Они могли бы найти и более lisp-way для этого. В любом случае, возможность применять эти функции для определяемых пользователем типов была-бы полезна.

Есть подозрение, что возможность наследования от built-in классов негативно бы сказалась на быстродействии и возможностях оптимизации компилятора.

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

>И еще, т.к. мы определяем другую функцию и пользуем другой символ, то все в стандартной библиотеке, что пользует #'+ и что нам надо расширить придется править по такой же схеме. Пример: incf, decf.

А стандарт не раскрывает детали реализации incf и decf. В каждой отдельной реализации CL они могут быть сделаны как угодно. Это в SBCL так может быть, но совсем не значит, что также в CLISP. В C++, например, нельзя утверждать, что '-' сделан с помощью '+' (сложить с указанным числом, поменяв знак ему)? Поэтому и '-' придется заменить. А что с a++ и a--?

Я предполагаю, что вопрос о переопределении стандартных операций стоял на голосованиях комиссии. Там люди сидели с опытом. :) Я считаю, что перегрузка стандартных оперций -- это ненужный синтаксический сахар, который может привести к путаницам.

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

Я предполагаю, что если переписать _весь_ лисп на CLOS - мы получим тормза не меньше чем у питона ;) И кодеры совсем за объектами не рассмотрят лиспа.

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

>Что вам мешает переопределить _любые_ функции _только_ для своего пакета?

Технически --- ничего. Я об этом уже писал выше. Повторю, что стандартные функции и макросы при этом будут пользовать версии из common-lisp. Пример: incf и decf. Эти макросы, если их тоже не перекрыть будут использовать common-lisp:+.

Вообще я не понимаю, чего тут на меня все ополчились. Мне нравится common-lisp. Но я пока еще не встречал идеальных во всех отношениях языков программирования. Common-lisp не исключение. Те недостатки о которых я писал это мелочи, но все равно недостатки. Большую проблему с точки зрения широкого применения common-lisp представляют проблемы с реализациями этого языка (где-то есть уникод, а где-то нету или плохо реализованный, где-то есть потоки, а где-то нет) и библиотеками под него (качественных библиотек маловато).

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

> Не надо путать sequence (абстрактный класс, или интерфейс в терминологии Java) и конкретные типы вроде list и array.

воистину. Можно создать экземпляр тырфейса в терминологии жабы? sequence - запросто: http://www.lisp.org/HyperSpec/Body/sec_17-1.html

> A sequence is an ordered collection of elements, implemented as either a vector or a list.

> Sequences can be created by the function make-sequence, as well as other functions

> В стандарте map, reduce, etc определяются именно в терминах sequence, но при этом они не generic функции, и их невозможно применять для других подклассов sequence. Это печально.

> create objects of types that are subtypes of sequence (e.g., list, make-list, mapcar, and vector).

Для какого из subtypes невозможно применять reduce например?

> То как map, reduce и иже с ними расширяются на used-defined типы в Ruby это лишь один из способов. Создатели common-lisp могли бы им последовать.

для лисппрог это просто _не_нужно_ потому что нету характерных для ооп ограничений, если только не вводить их специально. STL - это ведь не свойство языка, а библиотека, прально? Вот для аналогичных дел в лиспе тож библиотеки можно сделать. Я не ведаю, есть ли такие готовые или никому не было нужно. ИМХО второе. А разные sequences, списки и прочее - это _базовые_ элементы лиспа, как и функции для работы с ими. Ты ведь не жалуесся, что для базовых типов с++ (примитивных в ево терминологии), таких как целое или массив, нельзя переопределять операторы?

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

> Мне нравится common-lisp. Но я пока еще не встречал идеальных во всех отношениях языков программирования. Common-lisp не исключение. Те недостатки о которых я писал это мелочи, но все равно недостатки. Большую проблему с точки зрения широкого применения common-lisp представляют проблемы с реализациями этого языка (где-то есть уникод, а где-то нету или плохо реализованный, где-то есть потоки, а где-то нет) и библиотеками под него (качественных библиотек маловато).

А помочь? :)

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

> Вообще я не понимаю, чего тут на меня все ополчились.

я неополчился. Просто пытаюсь обяснить чё лисп - это не "с++ только луче", он _просто_ луче. И соответственно методы работы, аналогичные методам работыв в враждебном окружении, типо с++, тут негодяцо. С одной стороны, тя непременно арестуют если ты станеш без должных оснований разгуливать по городу в костюме химзащиты с надетым противогазом, с другой стороны, делать это всёодно неудобно, даже если очень хоццо.

> Большую проблему с точки зрения широкого применения common-lisp представляют проблемы с реализациями этого языка (где-то есть уникод, а где-то нету или плохо реализованный, где-то есть потоки, а где-то нет) и библиотеками под него (качественных библиотек маловато).

+1, проблем дествительно мнозе.

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

> Я предполагаю, что если переписать _весь_ лисп на CLOS - мы получим тормза не меньше чем у питона ;) И кодеры совсем за объектами не рассмотрят лиспа.

Это не обязан быть CLOS во всех своей красе. Просто больше стандартных функций могли бы быть generic. В уже писал о том, что это безусловно добавило бы работы компиляторо-писателям. Это действительно было бы сложней реализовать эффективно. Возможно, именно поэтому стандарт именно такой какой он есть.

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

> Это не обязан быть CLOS во всех своей красе. Просто больше стандартных функций могли бы быть generic. В уже писал о том, что это безусловно добавило бы работы компиляторо-писателям. Это действительно было бы сложней реализовать эффективно. Возможно, именно поэтому стандарт именно такой какой он есть.

Зыть, ещё раз... Дженерики работают с _классами_. Классы не равны типам. Для многих стандартных типов может быть _один_ класс (built-in). Поэтому без CLOS-а "во всех своей красе" от самого дна не обойтись.

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

>Я предполагаю, что если переписать _весь_ лисп на CLOS - мы получим тормза не меньше чем у питона ;) И кодеры совсем за объектами не рассмотрят лиспа.

Этого никто не предлагает. Это CLOS *тоже* построен на базе defgeneric. а не defgeneric на базе CLOS. :)

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

> Этого никто не предлагает.

Да нет, один анонимный товарищ практически этого и желает ;)

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

> Это CLOS *тоже* построен на базе defgeneric. а не defgeneric на базе CLOS. :)

Угу. Только generic-и работают с _классами_. Так что всё это "отдаёт" триединством :)

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

> > Не надо путать sequence (абстрактный класс, или интерфейс в терминологии Java) и конкретные типы вроде list и array.

> воистину. Можно создать экземпляр тырфейса в терминологии жабы? sequence - запросто: http://www.lisp.org/HyperSpec/Body/sec_17-1.html

Система типов common-lisp безусловно гораздо богаче чем в Java или C++, но в данном случае аналогия с жабщицкими интерфейсами достаточно близкая IMO. Экземпляр интерфейса (скажем Iterable) я могу создать в яве точно так же как и экземпляр sequence --- путем создания одного из конкретных подтипов. E.g. ArrayList vs. array

Правда, в стандарте, пoхоже, ничего не противоречит следующему определению sequence:

(deftype sequence () `(or list array))

> Для какого из subtypes невозможно применять reduce например?

Только что попробовал сделать user-defined class --- наследник sequence. Не дает. Если бы я мог сделать наследника sequence, то именно для него #'reduce бы не работал.

> для лисппрог это просто _не_нужно_ Не согласен. Возможность пользовать стандартные map, reduce, etc. для user-defined типов пригодилась бы. Пример я приводил. Жить без этого можно. И это совсем на большой недостаток, но он есть.

Безусловно это можно реализовать в виде сторонней библиотеки.

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

> Зыть, ещё раз... Дженерики работают с _классами_. Классы не равны типам. Для многих стандартных типов может быть _один_ класс (built-in). Поэтому без CLOS-а "во всех своей красе" от самого дна не обойтись.

Не понимаю что вы имели в виду, но я могу специализировать любую generic функцию любым встроенным типом. Никто не мешает мне сделать:

(defmethod plus ((first-arg cons) &rest args) (some-code-here))

(defmethod plus ((first-arg null) &rest args) (some-code-here))

(defmethod plus ((first-arg integer) &rest args) (some-code-here))

(defmethod plus ((first-arg fixnum) &rest args) (some-code-here))

(defmethod plus ((first-arg bignum) &rest args) (some-code-here))

И никаких слотов (разве что в внутри defmethod для обновления метаинформации о методах).

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

>Угу. Только generic-и работают с _классами_. Так что всё это "отдаёт" триединством :)

Нет. generic работает со всем, чей тип можно узнать: listp, integerp, stringp, numberp, atom (выше показаны примеры) и классов. По ним ведется диспечеризация при выборе метода. А классы -- это частный случай типа. Так что в данном случае, generic пофиг. В CLOS не классоориентированная концепция, а genericfunction-ориентрированная. defgeneric первична. CLOS порожден от нее. Аналог дженерика -- это обычная функция defun (...) со своим условным выполнением после проверки типов.

Хотя мне было бы спокойнее использовать

(defun matrix-add ...), (defun matrix-sub ...), ...

без всяких перегрузок. Выгоды переопределение не дает никакой. Говорю -- синтаксический сахар. :)

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

> Только что попробовал сделать user-defined class --- наследник sequence. Не дает.

ещёбы! sequence это не класс. Попробуй в с++ унаследовать массив

> Возможность пользовать стандартные map, reduce, etc. для user-defined типов пригодилась бы.

дык ты покажеш как в с++ пользовать стандартные [] от массива в user-defined type не переопределяя или не?

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

> Выгоды переопределение не дает никакой.

Выгода есть, хоть и небольшая. Если вместо matrix-add можно было-бы юзать +, то могли бы работать 1+, 1-, incf, decf (и возможные расширения вида mulf, divf) для моих типов.

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

> дык ты покажеш как в с++ пользовать стандартные [] от массива в user-defined type не переопределяя или не?

Мы похоже не понимаем друг друга. С самого начала я имел в виду что в STL и в Ruby я могу для user-defined типов пользовать стандартные алгоритмы, аналогичные map, reduce, etc. И для этого мне надо сделать минимум телодвижений. В STL мне надо определить итератор для моего user-defined типа, а в Ruby мне надо определить метод each. Т.е. мне не нужно для каждого user-defined типа реализовывать аналоги map, reduce, etc.

Я сожалел о том, что в common-lisp этого нету.

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

>Выгода есть, хоть и небольшая. Если вместо matrix-add можно было-бы юзать +, то могли бы работать 1+, 1-, incf, decf (и возможные расширения вида mulf, divf) для моих типов.

См. мой пост выше про '+' и '-'. Как это будет сразу работать 1+, 1-. incf, decf, когда для твоего типа должна быть определена единица? Если ты матрицу сделал 3 на 3, то что значит -- увеличить эту матрицу на 1? Это значит прибавить в ней единичную матрицу, где все единицы по диагонали? Или прибавить матрицу, где все элемены единицы? Или попытаться прибавить к матрице скаляр, что вызовет ошибку? Все эти операции придется переопределять. Умножение/сложение матриц и умножением/сложением чисел -- совершенно разные опреции. Что такое для числа i++ -- понятно (i := i + 1), а для матрицы разве эта операция следует из того, что ты только переопределил '+'?

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

> Не понимаю что вы имели в виду, но я могу специализировать любую
generic функцию любым встроенным типом.

КЛАССОМ, не типом! То, что во многих реализациях большинству
стандартных типов определены соответсвующие классы - "доброта"
авторов. Пример с вашим же определением _типа_:

* (deftype my-type () `(or list array))

MY-TYPE
* (defgeneric g1 (x))

#<STANDARD-GENERIC-FUNCTION G1 (0)>
* (defmethod g1 ((x t)) "Not defined")

#<STANDARD-METHOD G1 (T) {AD1AFC9}>
* (g1 1)

"Not defined"
* (defmethod g1 ((x my-type)) "My type")

debugger invoked on a SIMPLE-ERROR: There is no class named MY-TYPE.

Ок?

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

> См. мой пост выше про '+' и '-'. Как это будет сразу работать 1+, 1-. incf, decf, когда для твоего типа должна быть определена единица? Если ты матрицу сделал 3 на 3, то что значит -- увеличить эту матрицу на 1? Это значит прибавить в ней единичную матрицу, где все единицы по диагонали? Или прибавить матрицу, где все элемены единицы? Или попытаться прибавить к матрице скаляр, что вызовет ошибку? Все эти операции придется переопределять. Умножение/сложение матриц и умножением/сложением чисел -- совершенно разные опреции. Что такое для числа i++ -- понятно (i := i + 1), а для матрицы разве эта операция следует из того, что ты только переопределил '+'?

Да, я об этом не подумал. Эти сложности можно преодолеть (тем более что в более простых случаях, вроде кватернионов их нет), но тогда, похоже цена решения превышает выгоду.

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

> Нет. generic работает со всем, чей тип можно узнать: listp, integerp, stringp, numberp, atom (выше показаны примеры) и классов.

Ну почитатйте HyperSpec... Или определите свой _тип_ (не _класс_), и попробуйте для него определить метод... Выше я приводил пример.

> А классы -- это частный случай типа.

Классы - это классы. И к типм имеет лишь то отношение, что при создании класса автоматически создаётся тип с таким же именем (или type-of реагирует не только на типы, но и классы?)

> Так что в данном случае, generic пофиг.

Не пофиг, я приводил пример.

> Аналог дженерика -- это обычная функция defun (...) со своим условным выполнением после проверки типов.

Не типов - классов!!!

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

> КЛАССОМ, не типом! То, что во многих реализациях большинству стандартных типов определены соответсвующие классы - "доброта" авторов. Пример с вашим же определением _типа_:

Хороший пример, однако cons, number и многие другие встроенные типы являются классами, при этом они сами не являются экземплярами standard-class. T.e. мой тезис о том что многие стандартные функции могли бы быть generic без применения "тяжелого" CLOS, этот пример не отменяет.

Впрочем это не так уж и важно, что понимать под CLOS "во всей его мощи". Главное что так или иначе это могло быть сделано, и что это очень усложнило бы оптимизацию этих функций.

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

>КЛАССОМ, не типом!

Да, тут ты прав, похоже. deftype воспринимается defgeneric, если только он порожден от классов. А базовые типы просто продублировали в CLOS, так как они там обязательно нужны.

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

> Фиксед. Решил запостить сюда.

Вроде работает. Завтра погоняю на контрпримерах.

Такой вопрос: можно ли в алхимии быстро включить трассировку sql-запросов? В Лиспе это очень помогло при отладке.

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

> Мы похоже не понимаем друг друга.

похоже :(

> С самого начала я имел в виду что в STL и в Ruby я могу для user-defined типов пользовать стандартные алгоритмы, аналогичные map, reduce, etc.

Потому что в с++ (руби не знаю совсем, неведомо как там) есть примитивное типы, включяя массивы и операции с ними. А есть STL - библиотека шаблонов, которая изготовлена _поверх_ примитивных типов. Внутри себя она юзает примитивные типы, те же самые массивы и операции с примитивными типами +-=*<> итд. user-defuned типы в с++ нагромождаются _поверх_ STL в свою очередь. Операции с примитивными типами в с++ ты не можеш переопределять или наследовать, а наоборот, пользуешся ими внутри STL. В лиспе в базовый набор входят списки и всякие секвенции, ты точно также не можеш наследовать от них а только юзать их же для своих целей. Только соответствующий им набор базовых операций богаче, чем в с++, но это всё ещё базовые операции. Поэтому переопределять их ни (нормальной) возможности нету ни необходимости, точно так же как переопределять оператор [] для void * [] в с++. Необходимости переопределять нету потому что они не завязаны на какой-то конкретный тип, как в с++. Поэтому ты или юзаеш их или определяеш свои собственные. Как например если бы тя по каким-то причинам не устаивал оператор [] для void * [], то понадобилось бы делать функцию void * get (void * a [], int idx), а переопределить ты бы не смох. Если хош "нагромождения" как в с++, те сперва нужна библиотека (макросев или классов, неважно) а потом ужо наследовать от нё и переопределять.

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

> Хороший пример, однако cons, number и многие другие встроенные типы являются классами

Встроенных типов _намного_ больше, чем им в соотвествие поставленно классов, тем более что стандартом не определены эти классы - это "добрая воля" разработчиков. Да, у основных есть свои классы, но "закладываться" на это я бы не стал :)

> T.e. мой тезис о том что многие стандартные функции могли бы быть generic без применения "тяжелого" CLOS, этот пример не отменяет.

Тогда уж не "многие стандартные функции", а "подавляющее большинство". И _только_ для "чистых компиляторов" (а-ля sbcl). И в любом случае - это уже будет не CL (хотя и лисп :).

> Главное что так или иначе это могло быть сделано, и что это очень усложнило бы оптимизацию этих функций.

Для _только компилирующего_ лиспа - не очень. Всё равно или тип задан вручную, или надо ставить проверку типов в рантайме. Для всех остальных лиспов - "смерть при взлёте" :)

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

> Или взмутятся апологеты других ЯП :)

Ты имел в виду "БЯП"? :D

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

> Впрочем это не так уж и важно, что понимать под CLOS "во всей его мощи"...

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

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

> Да, тут ты прав, похоже.

Ага. Я на эти грабли умудрился стать ДВА раза. Теперь запомнил... ;)

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

> Потому что в с++ (руби не знаю совсем, неведомо как там) есть примитивное типы, включяя массивы и операции с ними. А есть STL - библиотека шаблонов, которая изготовлена _поверх_ примитивных типов. Внутри себя она юзает примитивные типы, те же самые массивы и операции с примитивными типами +-=*<> итд. user-defuned типы в с++ нагромождаются _поверх_ STL в свою очередь.

Можно долго спорить является ли STL частью языка C++ или нет, точно также можно спорить являются ли map, reduce и функции частью языка или этот "всего лишь" часть стандартной библиотеки. Это не важно.

map и reduce как операции можно определить не опираясь на какие-либо встроенные или нет типы. Они оперирует какой-то последовательностью (не важно как представленной) значений. Им в идеале наплевать как представлена эта последовательность, в виде ли цепочки cons-cell'ов или в последовательных ячейках вектора, или как-то еще. Им нужен только определенный набор базовых операций над этими последовательносями. Для reduce, к примеру, достаточно уметь каким-то образом пробежаться по последовательнсти. В Ruby эту базовую операцию предоставляет метод each, а в STL итераторы (forward iterator concept).

Предположим у меня нету стандартной функции reduce. В этом случае мне хотелось бы реализовать эту операцию ОДИН раз для ЛЮБЫХ видов последовательности. Что именно я буду использовать в качестве базового примитива для пробегания по последовательности не так важно. Важно чтобы я не повторял этот код если мне понадобится определить новый вид последовательности. Чтобы определяя этот самый новый вид последовательности мне было достаточно только определить базовые операции над ним (пробегание по ней, если мне нужен reduce).

Кстати, более-менее хороший пример как это можно сделать в common-lisp это series. Которые даже чуть было не попали в стандарт (см. cltl второй редакции).

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

> В этом случае мне хотелось бы реализовать эту операцию ОДИН раз для ЛЮБЫХ видов последовательности.

а в лиспе нету видов последовательности в том смысле, в каком есть унаследованные от STL классы в с++. Конечно ты можеш сделать свою функцию reduce используюя например elt, но смысла в этом никаково нету, потому что где работает стандартное elt там же работает и стандартное reduce.

> если мне понадобится определить новый вид последовательности.

ну безсмысленное это занятие определять новый вид последовательности в лиспе. Потому что существующие ничем не ограничивают, это ведь базовые ссущности. Чем твой новый вид может отличяцо от существующих? Только способом хранения данных, а в этом случяе стандартные алгоритмы всёодно бесполезны, они не будут знать как достичь данных в той форме, в которой ты их храниш.

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

> они не будут знать как достичь данных в той форме, в которой ты их храниш.

Будут. Способов масса. Один из них это по аналогии с Ruby такой:

(defgeneric each (sequence function)
  (:documentation "Calls FUNCTION for each element of SEQUENCE"))

(defmethod each ((sequence list) function)
  (mapl function sequence))

(defun primitive-reduce-variant (function sequence initial-value)
  (each sequence (lambda (x)
                   (setq initial-value (funcall function initial-value x))))
  initial-value)

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

>map и reduce как операции можно определить не опираясь на какие-либо встроенные или нет типы.

Так они так и определены. Есть произвольный список, к которому map можно сделать. В списке может быть объект любого вида: цифры, строчки, круги, квадраты, сферические кони в вакууме. Никаких ограничений. Гибкость, как карма у Будды :)

Спор начался, как я помню, с maphash. По какому-то недоразумению имя этой вещи начинается на map*. Но hash в CL -- это не список, не последовательность. Это таблица! Специальный неразбираемый тип данных, для которого определили отдельную функцию maphash. Нельзя по хэшу итерации устраивать, так как нет понятия индекса или последовательной связности. Есть только понятие ключа. А как по ключу итераторы устраивать? Поэтому maphash получает в качестве аргумента целиком хэш-таблицу, а не какой-то список, состоящий из его содержимого. Это совсем другая функция не из группы map*. С названием конфуз. Хэш надо рассматривать просто, как объект другой природы.

В языках C, C++ можно залезать в хэш на уровне указателей, но это уже (тут тонкая грань) работа не с хэшем, как с типом данных, а работа с хэшем на уровне реализации, коих может быть много разных.

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