суббота, 27 февраля 2010 г.

[prog.flame] Попытался прочитать книгу “97 Things Every Programmer Should Know”

Издательством O’Reilly была выпущена книга “97 Things Every Programmer Should Know”. Сколько-то там известных (как выяснилось, не для меня) программистов поделилось своими советами – якобы такими вещами, которые обязан знать каждый программист.

Каждый совет – две странички. При таком объеме на каждый совет я ожидал очень конкретных и акцентированных рекомендаций. Вроде очень простого, но весьма действенного совета для C/C++ программистов – в операциях сравнения на равенство всегда слева располагать константу. Т.е. писать if(0==i), а не if(i==0).

Но меня ждало разочарование. Ничего похожего на подобные концентрированные и сугубо практические рецепты не было. Были какие-то банальности вроде “лучше быть здоровым, чем больным, поэтому нужно следить за своим здоровьем” от каких-то неизвестных мне личностей. Временами противоречивые (кто-то советует не модифицировать старый код из соображений его улучшения, кто-то же советует стараться улучшать доставшийся вам в наследство код).

Я прочитал 117 страниц книги (из 257 в моей PDF-ке) и встретил всего три дельных главы – в одной приводились хорошие соображения для форматирования кода (совет от Yechiel Kimchi, стр.30), во второй – совет не писать глупости в комментариях в надежде, что их никто кроме вас не увидит (Cal Evans, стр.32), в третьей – предупреждение о том, что вещественные числа не являются обычными числами (Chuck Allison, стр.66). Мне это показалось слишком слабой концентрацией полезной информации на единицу объема.

пятница, 26 февраля 2010 г.

[work; prog] Нравоучение: делайте свои имитаторы чужих компонентов

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

Например, вам нужно обращаться к чужому Web-сервису – напишите его имитатор. Вам нужно обслуживать запросы чужих клиентов – напишите их имитаторы. Вам нужно запрашивать данные у чужой системы – напишите ее имитатор. Просто возьмите и напишите. Хотя бы самый простой и ограниченный в функциональности. Но чтобы вы могли докручивать его в любую нужную вам сторону. В любой момент. Никого не упрашивая и даже не ставя в известность.

Зачем это нужно делать? Затем, что если вы этого не сделаете, то рано или поздно вам придется обратиться к вашему партнеру с просьбой сымитировать ту или иную ситуацию. Мол, нужно проверить, что будет, если мы от вас получим три положительных ответа, затем через 0.25 секунды один ответ с кодом 0x543f, а еще через 0.155 секунды – еще 150 положительных ответов.

Так вот, когда у вас возникнет такая необходимость и вы обратитесь с подобной просьбой к своему партнеру, знаете что произойдет? Ваша просьба будет передана разработчику вроде меня (и это еще не самый плохой случай, я уверяю) и первая мысль у этого разработчика будет: “А нахер мне это надо?!!!”

И в лучшем случае, если вам повезет и у партнера будет серьезный стимул помогать вам, то со временем вашу просьбу удовлетворят, частично. Но ведь могут и вежливо послать, мол, “имитация не является штатным режимом работы нашего ПО и повторить предложенный сценарий не представляется возможным”.

А теперь представьте, что написанная вами система запущена в коммерческую эксплуатацию, работает под нагрузкой и вдруг случается большой трындец и наступает полный обертюр. Все на ушах, в чем дело не понятно, а разбираться нужно. Причем параллельно с вами разбираться в проблеме будут и партнеры. И не исключено, что они докопаются до причин раньше вас. Вот раскопают они, что если события происходят в последовательности X, Y, Z за временной интервал t, то вашей системе наступает кабздец. И на каком-нибудь совместном совещании в присутствии большого начальства поделятся своими подозрениями. И что вам останется? Вы-то сами не сможете работу своей системы проверить – нужно идти на поклон к партнеру, т.е. публично расписываться в собственной беспомощности. Даже если вы сами первыми вычислите эту злосчастную последовательность X, Y, Z – это все равно будут лишь ваши предположения. Проверить их самостоятельно вы будете не в состоянии.

А будь у вас имитатор компонента партнера вы бы оказались в более выигрышном положении. Вы смогли бы сами воспроизвести условия возникновения проблемы и проверить работу своей системы. И сделать это все оперативно, без публичного шума. Нашли бы проблему, залатали ее и при разборе полетов бы доложили: “Да, был у нас злобный баг. Он проявлялся при таких-то условиях. Мы его уже исправили. Сейчас все работает.” И пусть партнер проверяет сколько хочет (если сможет, ведь имитация не является штатным режимом работы его ПО).

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

[life.photo] Сложнее ли засадить в бутылку дубинку, а не парусник?

Помню, как в детстве я приходил в восторг от парусников в бутылках:

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

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

Снимок найден в подборке фотографий необычных бутылок.

четверг, 25 февраля 2010 г.

[prog.humour] Как взять первую часть файла в Windows с помощью подручных средств?

Вчера вечером пытался решить простенькую задачку: на нетбуке из большого (порядка 8Gb) eMule-овского временного файла нужно было взять первых 100Mb и записать на флэшку, чтобы сделать preview на телевизоре (сам нетбук 1020p видео не тянет в принципе). Сделать это нужно было с помощью подручных средств – штатных утилит Windows Vista да Far с Rar-ом. Никаких средств разработки на нетбуке не было. А аналогов Unix-овых head и tail в Windows, насколько мне известно, нет.

Первая мысль была поискать в Интернете какой-нибудь готовый splitter для файлов. Но это мне показалось неспортивным. Поэтому обошелся rar-ом. Сначала запускается архивация временного файла в многотомный архив с нулевым сжатием (rar a –m0 –v100m), которая прерывается после формирования первой части архива. Затем из этой первой части на флэшку распаковывается то, что там оказалось – как раз приблизительно 100Mb из большого временного файла :)

Голь, как водится, на выдумки хитра. На том и стоим, а точнее – лисапеды придумываем ;)

Но интересно, можно ли было сделать тоже самое при наличии Windows Vista и Far-а по-другому?

[sport; humour] Найти голову за 5 секунд

А как быстро у вас получиться найти на этой фотографии голову японской фигуристки Мики Андо? ;)

Снимок найден в очередном выпуске WSJ’s Pictures of the Day.

среда, 24 февраля 2010 г.

[life.humour] Когда б мы знали по каким поисковым фразам читатели приходят к нам в блоги ;)

Когда-то давно опубликовал у себя фото женщины с гипертрофированными мышцами, и с тех пор приличная доля посетителей приходит ко мне в блог через поисковики по фразам вроде “культуристы”, “женщины-культуристы” и “мужики” :)

Не так давно описал свое посещение блога Сергея Доли. Теперь лидером среди поисковых запросов по статистике стали фразы “Сергей Доля”, “Компания Сергея Доли” и “Сергей Доля компания”.

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

Первая: “что будет если застрелиться”.
Поражает вера людей в то, что Google знает что-то о другой стороне того длинного тоннеля :)

Вторая: “мебель из женщин”.
Ну тут остается только присоединиться к автору этого вопроса, чтобы самому хоть краем глаза увидеть, что же это такое…

PS. Вот так ненавязчиво я употребил все самые популярные поисковые фразы в одной заметке – теперь уж отбоя от посетителей не будет! ;)

вторник, 23 февраля 2010 г.

[prog] Squeryl – SQL-подобный DSL для Scala

Состоялся публичный релиз проекта Squeryl – встроенного в Scala DSL-я для представления SQL-выражений. Проект молодой, что из всего этого получится пока не понятно, но посмотреть интересно. Ниже я приведу большой фрагмент из демонстрации Squeryl, для создания впечатления ;)

У меня никакого мнения по поводу этого проекта нет. Как и нет какого-то определенного мнения вообще по поводу подобных DSL-ей. В C++ с РСУБД я работаю через библиотеку OTL, там все очень примитивно – код приходится адаптировать под каждую БД (поскольку в MSSQL, Oracle и PostgreSQL есть собственные расширения SQL) и никаких гвоздей! ;)

А теперь обещанный фрагмент из раздела Compilable Examples:

package org.squeryl.demos;
 
import org.squeryl.PrimitiveTypeMode._
import org.squeryl.adapters.H2Adapter
import org.squeryl.{Query, Session, KeyedEntity, Schema}
import java.sql.SQLException
import org.squeryl.dsl.GroupWithMeasures

// The root object of the schema. Inheriting KeyedEntity[T] is not mandatory
// it just makes primary key methods available (delete and lookup) on tables.
class MusicDbObject extends KeyedEntity[Long] {
  var id: Long = 0
}
 
class Artist(var name:String) extends MusicDbObject {
 
  // this returns a Query[Song] which is also an Iterable[Song] :
  def songs = from(MusicDb.songs)(s => where(s.artistId === id) select(s))
 
  def newSong(title: String, filePath: Option[String], year: Int) =
    MusicDb.songs.insert(new Song(title, id, filePath, year))
}
 
// Option[] members are mapped to nullable database columns,
// otherwise they have a NOT NULL constraint.
class Song(var title: String, var artistId: Long, var filePath: Option[String], var year: Int) extends MusicDbObject {
 
  // IMPORTANT : currently classes with Option[] members *must* provide a zero arg
  // constructor where every Option[T] member gets initialized with Some(t:T).
  // or else Squeryl will not be able to reflect the type of the field, and an exception will
  // be thrown at table instantiation time.
  def this() = this("", 0, Some(""),0)
 
  // the schema can be imported in the scope, to lighten the syntax :
  import MusicDb._
  
  // An alternative (shorter) syntax for single table queries :
  def artist = artists.where(a => a.id === artistId).single
 
  // Another alternative for lookup by primary key, since Artist is a
  // KeyedEntity[Long], it's table has a lookup[Long](k: Long)
  // method available :
  def lookupArtist = artists.lookup(artistId)
}
 
class Playlist(var name: String, var path: String) extends MusicDbObject {
 
  import MusicDb._
 
  // a two table join :
  def songsInPlaylistOrder =
    from(playlistElements, songs)((ple, s) =>
      where(ple.playlistId === id and ple.songId === s.id)
      select(s)
      orderBy(ple.songNumber asc)
    )
 
  def addSong(s: Song) = {
 
    // Note how this query can be implicitly converted to an Int since it returns
    // at most one row, this applies to all single column aggregate queries with no groupBy clause.
    // The nvl function in this example changed the return type to Int, from
    // Option[Int], since the 'max' function (like all aggregates, 'count' being the only exception).
    val nextSongNumber: Int =
      from(playlistElements)(ple =>
        where(ple.playlistId === id)
        compute(nvl(max(ple.songNumber), 0))
      )    
    
    playlistElements.insert(new PlaylistElement(nextSongNumber, id, s.id))
  }
 
  def removeSong(song: Song) =
    playlistElements.deleteWere(ple => ple.songId === song.id)
 
  def removeSongOfArtist(artist: Artist) =
    playlistElements.deleteWere(ple =>
      (ple.playlistId === id) and
      (ple.songId in from(songsOf(artist.id))(s => select(s.id)))
    )
 
  // New concept : a group query with aggregate functions return GroupWithMeasures[K,M]
  // where K and M are tuples whose members correspond to the group by list and compute list
  // respectively.
  def _songCountByArtistId: Query[GroupWithMeasures[Long,Long]] =
    from(artists, songs)((a,s) =>
      where(a.id === s.artistId)
      groupBy(a.id)
      compute(count)
    )
 
  // Queries are nestable just as they would in SQL
  def songCountForAllArtists  =
    from(_songCountByArtistId, artists)((sca,a) =>
      where(sca.key === a.id)
      select((a, sca.measures))
    )
 
  // Unlike SQL, a function that returns a query can be nested
  // as if it were a query, notice the nesting of 'songsOf'
  // allowing DRY persistence layers as reuse is enhanced.
  def latestSongFrom(artistId: Long) =
    from(songsOf(artistId))(s =>
      select(s)
      orderBy(s.id desc)
    ).headOption
 
  def songsOf(artistId: Long) =
    from(playlistElements, songs)((ple,s) =>
      where(id === ple.playlistId and ple.songId === s.id and s.artistId === artistId)
      select(s)
    )
}
 
 
class PlaylistElement(var songNumber: Int, var playlistId: Long, var songId: Long)
 
 
class Rating(var userId: Long, var appreciationScore: Int, var songId: Int)
 
 
object MusicDb extends Schema {
 
  val songs = table[Song]
  val artists = table[Artist]
  val playlists = table[Playlist]
  val playlistElements = table[PlaylistElement]
  val ratings = table[Rating]
 
  // drop (schema) is normaly protected... for safety, here we live dangerously !
  override def drop = super.drop
}

[prog.humour] Это случайное число в двадцать раз случайнее ;)

Улыбнула фраза из статьи в Dr.Dobb про новый способ генерации случайных чисел:

The researchers' experiments with an array of flip-flop units show that for small arrays the extra layer makes the random number almost twenty times more "random" than conventional methods.

Т.е. эксперименты ученых показали, что с использованием их новой техники случайные числа получаются в двадцать раз случайнее, чем обычно. При всей серьезности проблемы генераторов случайных чисел (помнится, был высказан афоризм: генерация случайных чисел слишком важна, чтобы оставлять её на волю случая *), утверждение “в двадцать раз случайнее” напоминает мне анекдот про рекламу суперкомпьютеров Cray:

Новый суперкомпьютер Cray настолько быстр, что выполняет бесконечный цикл всего за 4.87 секунды!

понедельник, 22 февраля 2010 г.

[life.photo] Две умиротворенные гориллы из Нью-Йоркского зоопарка

Блин, прям идиллия какая-то ;)

Посмотришь на такое фото и отпадут последние сомнения в происхождении человека от обезьяны :)))

воскресенье, 21 февраля 2010 г.

[life.photo] Российские пейзажи Анатолия Андреева

В продолжение темы, затронутой в прошлый раз, публикую пейзажи Анатолия Андреева.

Сначала без золоченных куполов ;)

Перед грозой...
«Перед грозой...»

Теплое утро января
«Теплое утро января»

Утро в дубовой роще
«Утро в дубовой роще»

Уголок озера Хвощно в Любытинском р-не.
«Уголок озера Хвощно в Любытинском р-не.»

Застывшие краски.
«Застывшие краски.»

Тишина. Начало осени.
«Тишина. Начало осени.»

И несколько с куполами (куда же в России без них) ;)

Вечерело. Николо-Вяжищский монастырь
«Вечерело. Николо-Вяжищский монастырь »

Февраль. Полдень.
«Февраль. Полдень.»

Вид на Юрьев монастырь c башни Кокуй новгородского кремля.
«Вид на Юрьев монастырь c башни Кокуй новгородского кремля.»

Витославлицы зимой.
«Витославлицы зимой.»

Вид на Витославлицы перед грозой.
«Вид на Витославлицы перед грозой.»

А в завершение небольшое ажурное макро:

Хранитель бриллиантов
«Хранитель бриллиантов»

Другие работы Анатолия Андреева можно посмотреть на photosight.ru и photoline.ru.