Oct. 3rd, 2011

kaipa: (Default)
Напечатал 5 (пять!) килограммов фотографий. Как-то раньше не думал, что фотографии можно измерять килограммами. Но сколько нам открытий чудных.. Всего примерно 750 карточек размера 15x20(21), в основном, семейных, плюс пара поездок. Еще зимой жена проделала грандиозную работу с архивом и отобрала около 1000 фотографий для печати. Больше полугода я тянул, и вот, наконец. Сейчас добавились еще, так что примерно 500 еще осталось напечатать. Лучшее пойдет в альбомы. По деньгам -- дешевле, чем на принтере. Одна фотография стоит меньше 13 руб, печать на оборудовании Fujifilm.

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

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

Несколько любимых фотографий с Бали, которые вы вряд ли увидите в путеводителях. На Бали приезжает очень много туристов, но многие не выходят особо за пределы гостиницы. Мы за две поездки объехали почти весь остров, и во многих местах были единственными иностранцами. При печати "обнаружилось" гораздо больше фотографий, которые стоят того, чтобы их выложить в Сеть (эти лежат давно). Займусь на досуге. Прошу прощения за нечеткие названия мест, кому интересно, могу дать точные координаты, просто подзабыл, надо карту смотреть, по карте я точно помню, где и когда мы были.

1. Север Бали 2007г. Вид на Яву.

+6 )
kaipa: (Default)
Прошу прощения, если эта тема избита и изъезжена вдоль и поперек, но мне стоило немалого труда "переварить" в себе, зачем это нужно. Начну с конца, вернее, с середины. В Скале функциональный трейт Function1 -- контрвариантный по аргументу, и ковариантный по результату.

trait Function1[-P, +R] {
def apply(p: P): R
}


Это не какая-то экзотика, любая параметризированная функция от одного переменного в Скале ведет себя именно так. Что это значит? На уровне "наивных" определений, ковариантность, обозначаемая модификатором "+", -- это естественное движение "вниз" по иерархии типов, от более общих к более частным, а контрвариантность, обозначаемая модификатором "-", -- наоборот, вверх, от производных типов к базовым. Вроде бы все просто, но не совсем.

Сами параметры или аргументы функций -- естественно, ковариантны. Т.е. F(p: T) можно вызвать для любого p, производного от T типа. Однако сам функциональный тип ведет себя совершенно по-другому, проще всего это продемонстрировать таким выражением:

val f: Function1[P, R] = new Function1[Psup, Rsub] { … }, где Psup -- супер-тип от P, а Rsub -- производный тип от R.

Если подумать, то это выглядит довольно странно. Вот у нас есть функция из P в R, и ее частным случаем является функция из типа, более общего, чем P, в менее общий, чем R. Тем не менее, все очень рационально. Сначала, я поймал понимание, почему это так, практически сразу, но потом стал пытаться объяснить самому себе в деталях и запутался. Помог вернуть голову на место пример из документации. Тем не менее, я предложу простое аналитическое обоснование, изначально пришедшее мне в голову (его можно представить и геометрически).

Пусть F: P → R, т.е. p ∈ P => F(p) ∈ R

Рассмотрим, F' : P` → R' : P` ⊃ P, R` ⊂ R.

Отсюда, p ∈ P => p ∈ P` => F`(p) ∈ R`=> F`(p) ∈ R.

То есть F` -- частный случай F на ее области определения.

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

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

trait List[+A] {
def cons(hd: A): List[A]
}

То есть у нас совершенно естественное желание, чтобы List[String] был подтипом List[Object], и поэтому используем ковариантный модификатор '+'. Однако, параметризированная функция cons содержит тип A в контрвариантной позиции, и компилятор выдаст ошибку. Что делать? Для этого в Скале есть трюк, позволяющий определять границы типов, а именно:

trait List[+A] {
def cons[B >: A](v: B): List[B]
}

Это означает, что метод cons определен для некоторого типа B, который является супер-типом от А, или A -- нижняя граница или lower bound для типа B. В частном случае, B может совпадать с A, но в общем случае функция cons может вернуть более общий тип B, что удовлетворяет естественному ощущению, что если в один список добавлять числа и строки, то получится список из более общих объектов.

Аналогичным образом в некоторых случаях целесообразно определять верхнюю границу или upper bound параметризированного типа.

Несколько англоязычных ссылок:
Scala Type System
Scala covariance/contrvariance at StackOverflow

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

Profile

kaipa: (Default)
kaipa

April 2017

S M T W T F S
       1
2345678
9101112131415
16171819202122
23242526272829
30      

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Oct. 22nd, 2017 10:37 pm
Powered by Dreamwidth Studios