«Горбушка»

На сайте 3Dnews появилась статья «Легендарная „Горбушка“: вид с другой стороны прилавка», в статье интервью с один из продавцов на «Горбушке». Вот несколько выдержек:

Илья Тараканов: С места в карьер — вопрос: почему такие цены? Ведь приходит человек и видит — дорого.

Иван: А почему дорого-то? Например, в том же «Связном», аппарат стоит 30 тысяч, а у меня он выставлен за 20. Если вещь не очень новая — то может быть и за 15. Согласись — полцены — это ведь совсем не дорого. Повторюсь, бывает, что телефон или ноутбук в идеальнейшем состоянии, муха не сидела, — тогда цена 20 тысяч рублей, это неплохо.

Может у этого продавца всё именно так и обстоит, но он не показатель. Ходили мы с женой по Горбушке пару недель назад и искали где бы купить смартбук Тошиба АЦ-100. Нашли в нескольких местах, всё очень дорого. Типичный разговор:

— Здравствуйте, Тошиба АЦ-100 есть? — Есть, да. — А почём? — Могу за двенадцать отдать… — Двенадцать?! Почему так дорого?! — показываю продавцу «Яндекс.Маркет» с телефона, там цена с 6,5 тысяч начинается — Ну вы в интернете… не сравнивайте. — Почему?..

В ответ молчание. Я не стал спрашивать почему я не должен сравнивать.

Иван: Знаешь, сам раньше тоже думал, что продавцы — хамы, а когда сам какое-то время поработал на рынке — а в торговле я уже пару десятков лет — понял, что иногда банально сдают нервы. Работа, как ты понимаешь, далека от спокойной, ходит много неадекватных людей, и с каждым годом все больше. Продавцы, грубо говоря, садятся на валерьянку. Вот, например, приходит человек и говорит «Nokia N72». И на меня смотрит. Что я на это должен ответить? Я на него просто смотрю, а он начинает раздражаться. Мне просто хочется, чтобы он грамотно сформулировал, что хочет.

Я не продавец, но как-то мгновенно догадался, что человек хочет «Нокию Н72». По себе знаю — обходишь пару десятков павильонов в разных концах рынка, убиваешь на это пару часов, соответственно, хочется сэкономить время — пришёл, спросил «Нокия Н72», получил ответ «нету» и пошёл дальше.

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

26 комментариев

QR-коды в казанском кремле

На светлой каменной стене закреплена табличка с надписью «Мечеть Кул Шариф» и QR-кодом, рядом — окно с металлической решёткой.
Белая табличка на кирпичной стене с надписью «Южный корпус Пушкинского двора», QR-кодом и ссылкой http://www.pushkin-dvor.ru/703.
Две информационные таблички с QR-кодами: «Комплекс Пушечного двора» (m.kazan-kremlin.ru/700) и «Главный корпус Пушечного двора» (m.kazan-kremlin.ru/701).

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

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

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

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

13 комментариев

«Go»: указатели и арифметика

Одна из особенностей «Go», о которой пишут в большинстве статей по языку, это наличие в языке указателей, но отсутствие арифметики с ними. Вот, например, цитата из статьи «Go For C++ Programmers»:

Go has pointers but not pointer arithmetic. You cannot use a pointer variable to walk through the bytes of a string.

В самом деле, если попробовать скомпилировать следующий пример, то нас ждёт неудача:

package main
func main() {
    var pointer *[]uint8 = new([]uint8)
    pointer++ // invalid operation: pointer += 1 (mismatched types *[]uint8 and int)
}

Никакими преобразованиями базовых типов не добиться чтобы язык разрешил прибавить хоть что-то к указателю. Я написал «базовых» и это неслучайно. Язык действительно не содержит арифметики с указателями, но в языке есть стандартные модули.

В модуле «unsafe» есть специальный тип «Pointer», в руководстве по использованию которого написано следующее:

1) A pointer value of any type can be converted to a Pointer.
2) A Pointer can be converted to a pointer value of any type.
3) A uintptr can be converted to a Pointer. 4) A Pointer can be converted to a uintptr.

В свою очередь, uintptr встроен в язык и это не указатель, это обычный числовой тип, пусть вас не смущает слово «ptr». Указатель напрямую нельзя преобразовать к типу uintptr или обратно, но можно это сделать через unsafe.Pointer, как видно из цитаты выше.

И вот что получается:

package main

import "fmt"
import "unsafe"

func main() {
    e := [...]uint8{11,22,33} // создаю массив из трёх байт — 1, 2, 3
    p := &e[0] // p — указатель на первый элемент массива (тип *uint8)

    fmt.Println(*p) // выводит «11»

    p = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + 1)) // указатель преобразован в число, прибавляется «1», преобразуем обратно в указатель
    fmt.Println(*p) // выводит «22»

    s := "12345" // строка, содержащая «12345»
    *(*uintptr)(unsafe.Pointer(&s))++ // сдвигаем адрес, на который ссылается «s» 

    fmt.Println(s, len(s)) // «2345», но длина всё равно 5 (!), в последнем байте неотображаемый символ
}

10 комментариев

«Go» паникует

Попробую рассказать ещё об одной вещи, которая показалась мне в «Go» странной, дополню предыдущий пост.

Иногда, скомпилированная программа, написанная на «Go» «паникует»:

bolk-osx:Sample bolk$ 6g t.go && 6l t.6 && ./6.out
panic: runtime error: index out of range

runtime.panic+0xac /Users/bolk/go/src/pkg/runtime/proc.c:1083
    runtime.panic(0x11ea8, 0xf8400011f0)
runtime.panicstring+0xa3 /Users/bolk/go/src/pkg/runtime/runtime.c:116
    runtime.panicstring(0x295e6, 0xce63)
runtime.panicindex+0x25 /Users/bolk/go/src/pkg/runtime/runtime.c:73
    runtime.panicindex()
main.main+0x5e /Users/bolk/Проекты/Sample/t.go:6
    main.main()
runtime.mainstart+0xf /Users/bolk/go/src/pkg/runtime/amd64/asm.s:77
    runtime.mainstart()
runtime.goexit /Users/bolk/go/src/pkg/runtime/proc.c:150
    runtime.goexit()
----- goroutine created by -----
_rt0_amd64+0x8e /Users/bolk/go/src/pkg/runtime/amd64/asm.s:64

То есть возникает ошибка, которая аварийно завершает выполнение программы. Самый простой способ этого добиться — вызов «panic», более реальный — обратиться за границу массива:

package main

func main() {
    arr := []int{1,2,3}

    print(arr[1000])
}

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

Я спросил пару человек, незнакомых с «Go», что, по их мнению, язык должен бы предоставить программисту для решения этой проблемы, оба человека назвали обработку исключений. К сожалению, тут исключений нет. Подход к решению этой проблемы у «Go» чем-то напоминает Visual Basic, а именно конструкцию On Error GoTo line.

В «Go» есть поистине замечательная штука — defer, с её помощью можно вызвать функцию когда управление покидает определённую зону видимости. Для языка без деструкторов (а в «Go» их нет) это очень удобно — можно закрывать файлы, сокеты и прочее в таком полуавтоматическом режиме — важно не забыть поставить нужный вызов только один раз.

Функции с defer будут вызваны когда управление покидает выбранную нами зону по любой причине, в том числе, когда произошла «паника». В функции можно проверить, была ли она вызвана нормальным ходом событий или что-то пошло не так, вызов этой проверочной инструкции (он называется «recover») «успокаивает панику».

Выглядит это вот так:

package main

func main() {
    defer func() {
        if r := recover(); r != nil {
            print("Recovered")
        }
    }()

    arr := []int{1,2,3}

    print(arr[1000])
}

Происходит тут следующее: в зоне вызова функции «main» (тут, как в Си, при запуске вызывается «main») определена анонимная функция, которая тут же вызвана как defer, при возникновении ошибки (обращение за границу массива в предпоследней строке), управление покидает зону видимости функции «main» из-за чего вызывается наша анонимная функция, которая проверяет из-за чего она была вызвана (присваивает значение, полученное из recover, переменной „r“ и проверяет его на nil), чем «успокаивает панику», если она была, и выводит сообщение «Recovered» в этом случае.

6 комментариев

Послевкусие от «Go»

Должен сказать, что у «Go», как, наверное, у любого нового языка программирования, в изобилии свои, модные, ни на что не похожие тараканы. Небольшой пример приведу перед сном.

У «Go» есть хеши, называются «map», описываются несколько необычно: нужно описать не только тип значений, но и тип ключей. «Go» язык строгий, для него это важно:

package main
import `fmt`

func main() {
    arr := map [string] byte { `a`: 1, `b`: 2}
    fmt.Println(arr)
}

Эта простенькая программка выведет на экран что ожидается: «map[a:1 b:2]», то есть строковое представление нашей переменной типа «map». Давайте представим, что из переменной «arr» нужно удалить какое-то значение по ключу. Что для этого нужно сделать, как вы думаете?

Можете прочитать руководство и попробовать найти нам функции «del», «delete», «remove», «destroy», что угодно, ничего такого вы там не найдёте. Попытка присвоить «nil» ни к чему не приведёт — язык не позволит это сделать. Что делать?

Можно, конечно, пересоздать переменную с новым значениями, но есть способ… э… лучше. Почему я в этом не уверен будет понятно дальше.

Каждый элемент переменной типа «map» содержит специальную магию. Если запросить у элемента не одно, а два значения, то вернутся два — значение элемента и флаг, существует ли он:

val, exists := arr["a"]
fmt.Println(val, exists) // выведет «1 true»

Чтобы удалить значение из хеша надо присвоить второму значению «false»:

arr["a"] = arr["a"], false // a["a"][1] = false не сработает, у него другой смысл
fmt.Println(arr) // выдаёт map[b:2]

val, exists = arr["a"]
fmt.Println(val, exists) // выведет «0 false», значение сброшено и «не существует»

Если запросить у хеша значение с ключом, которого никогда в нём не было, эффект будет тот же — нулевое или эквивалентное ему значение и «false», тут всё логично.

Выбор такого решения для удаления значения мне кажется странным.

На всякий случай, приведу место из официального руководства, где это всё описано, чтобы не возникло ощущение, что я просто его прочитал невнимательно:

Similarly, if an assignment to a map has the special form a[x] = v, ok and boolean ok has the value false, the entry for key x is deleted from the map; if ok is true, the construct acts like a regular assignment to an element of the map.

8 комментариев

Язык программирования «Go»

В этом году я решил выучить язык программирования «Go». Язык разработан в стенах «Гугла» и это один из проектов, который у «Гугла», что называется, «не взлетел». В компании не очень-то занимаются его продвижением и поддержкой, если ничего не изменится, то язык, по всей видимости, будет обречён на забвение.

Самое интересное в языке — это то, как он запросто справляется с параллелизацией задач, но я пока не успел это попробовать, видел лишь на примерах. Выглядит очень круто. Интересен он ещё и тем, что компилируется (архитектуры AMD64, i386 и ARM), но при этом сам умеет управлять памятью. Ошибочно считается, что в языке нет «традиционных» указателей (с арифметикой), это не так, там они есть, просто спрятаны в модуль.

Компиляция, как утверждается, даёт хороший прирост скорости (всего лишь в 5-10 раз медленее Си и в сотни раз быстрее PHP). При этом моя небольшая программа (см. ниже) скомпилировалась в файл размером 5,8 мегабайт.

Язык сильно меняется со временем, можно заметить, что примеры кода, которым только год, уже не компилируются. Документация по некоторым модулям не просто скудная, её нет, взгляните, к примеру, на функцию Accept модуля syscall.

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

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

Оказалось, что на «Go» программа решается буквально в несколько строк, поэтому это было неинтересно. Я усложнил задачу — после запуска программа должна была сказать по какому IP-адресу можно будет зайти браузером.

Читать дальше.

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

Я использовал модуль syscall и вызвал системный вызов ioctl с параметром SIOCGIFCONF. Из-за того, что язык самовольно выравнивает структуру (struct) по границе 8 байт (надо бы проверить будет ли тот же эффект на архитектуре с 32 битами), я очень долго не мог понять почему мой вызов даёт ошибку.

Дальше всё просто — я получаю все адреса IPv4 (можно и IPv6, но мне пока не хочется возиться), если их несколько, то убираю из них те, что относятся к Private IP (10/8, 172.16/12, 192.168/16) и возвращаю один из тех, что остались. В противном случае, возвращаю тот единственный, что у меня в наличии.

Сама программа ниже:

// httphere 0.1. Написана Евгений Степанищевым, май 2011. //bolknote.ru
package main

import (
    "http"
    "flag"
    "strconv"
    "os"
    "syscall"
    "unsafe"
    "strings"
    "path"
    "path/filepath"
)

const IF_BUFFER int = 2048

type ifconf struct {
    _   int32
    len int32              "Размер буфера"
    ifr unsafe.Pointer  "Указатель на буфер"
}

func exit(message string, code int) {
    os.Stderr.WriteString("Error: " + message + "\n")
    os.Exit(code)
}

/* Проверка входного параметра (порт) на допустимые значения */
func checkargs(port int) {
    if port > 65535 || port < 1 {
        exit("invalid port range", 1)
    }

    if syscall.Geteuid() > 0 && port < 1024 {
        exit("if you want to use ports lower than 1024, you must run httphere as the root user", 3)
    }
}

/* преобразование IP-адреса в строку */
func joinip4(ip []byte) string {
    var ipstr []string

    for _, s := range ip {
        ipstr = append(ipstr, strconv.Itoa(int(s)))
    }

    return strings.Join(ipstr, ".")
}

/* получаем список IPv4-адресов на внешних интерфейсах */
func getallip4() (iplist []([]byte)) {
    fd, errno := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
    if err := os.NewSyscallError("socket", int(errno)); err == nil {
        defer syscall.Close(fd)

        ifr := new([IF_BUFFER]byte)
        ifc := ifconf{len: int32(unsafe.Sizeof(*ifr)), ifr: unsafe.Pointer(ifr)}

        ifcwopadding := uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(&ifc)) + 4))
        _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.SIOCGIFCONF), ifcwopadding)

        if err := os.NewSyscallError("ioctl", int(errno)); err != nil {
            return
        }

        for i := int32(0); i < ifc.len; {
            i += 16 /* skip interface name */
            length, family := int32(ifr[i]), ifr[i+1]

            sockaddr := ifr[i+2:i+length]
            i += length

            if family == syscall.AF_INET {
                iplist = append(iplist, sockaddr[2:6]) 
            }
        }
    }

    return
}

/* на вход подаётся список IPv4-адресов, если их несколько, то функция выбрасывает локальные.
   на выходе — один IPv4 
*/
func filterip4(iplist []([]byte)) []byte {
    switch len(iplist) {
        case 0:
            return []byte{127, 0, 0, 1}

        case 1:
            return iplist[0]
    }

    var private []byte = nil

    for _, ip := range iplist {
        if ip[0] == 192 && ip[1] == 168 || ip[0] == 10 || ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31 {
            private = ip
        } else if ip[0] != 127 {
            return ip
        }
    }

    if private != nil {
        return private
    }

    return iplist[0]
}

func main() {
    port := flag.Int("port", 8080, "http port address (1-65535)")
    flag.Parse()
    checkargs(*port)

    portstr := strconv.Itoa(*port)
    ip := joinip4(filterip4(getallip4()))

    os.Stdout.WriteString("Listen on " + portstr + ". Go to http://" + ip + ":" + portstr + "/\n")

    http.Handle("/", http.HandlerFunc(handler))
    if err := http.ListenAndServe(":" + portstr, nil); err != nil {
        exit(err.String(), 2)
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
    cwd, _ := os.Getwd()
    path := path.Join(cwd, filepath.Clean(r.URL.Path))

    http.ServeFile(w, r, path)
}

У программы есть единственный параметр (он выводится, если запустить программу с ключом „—help“) — можно указать порт, который программа будет слушать (по умолчанию использутеся 8080).

Программа прерывается по Ctrl+C.

17 комментариев

Постановление правительства Москвы о порядке переустройства и (или) планировки жилых и нежилых помещений в жилых домах на территории города Москвы

Я как-то искал что же действует сейчас вместо закона Москвы от 29 сентября 1999 года №37 «О порядке переустройства помещений в жилых домах на территории города Москвы», который был отменён 7 декабря 2005 года законом города Москвы №66.

Нашёл норматив города Москвы по эксплуатации жилищного фонда (ЖНМ-2005/01) «Регламент оформления и проведения переустройства и (или) перепланировки жилых и нежилых помещений в жилых домах на территории города Москвы» (это в Приложении №2 к постановлению Правительства Москвы от 25 сентября 2007 года, № 831-ПП).

Цитирую:

В жилых домах в период проведения ремонтно-строительных работ не допускается:

— производить работы в воскресные и праздничные нерабочие дни; — начинать работы, сопряженные с шумом, ранее 9.00 и (или) заканчивать их позднее 19.00; — применять при производстве работ оборудование и инструменты, вызывающие превышение нормативно допустимого уровня шума и вибрации;

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

Допустимый уровень шума регламентируется СанПиН 2.1.2.2645-10 (вступил в силу 15 августа 2010 года), в Приложении №3. Там указан, например, максимальный уровень звука с семи утра до 23 вечера, он должен быть не выше 55 дБА.

«дБА» — это «децибелл акустический», уровень звука, скорректированный с учётом восприятия его человеком. Чтобы было с чем сравнить, 55 — это примерно уровень разговорной речи, а на 70 дБА шумит квартирный пылесос.

8 комментариев

Бытовой ислам

Во всём мире интерес к исламу сейчас носит нездоровый характер. Я почти 30 лет прожил в республике Татарстан, где одна из главенствующих религий — ислам (булгары, которых сейчас называют «казанские татары», приняли ислам ещё в 922 году).

В этой связи мне частенько задают вопросы как в Татарстане с исламским экстремизмом. Отвечаю сразу всем: никак, друзья.

Ислам в этой республике крайне либерален и большинство татар такие же мусульмане, как большинство русских — православные. То есть относят себя к этой религии, но не исполняют обрядов и, по честному, скорее агностики, чем апологеты — верят что «что-то такое есть», считая это достаточным.

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

Путешествуя по мусульманским странам за границей (Турция, Египет, Мальдивы, Иордания, Палестина), я понял, что бытовой ислам там выражен куда сильнее, чем в Татарстане. При этом, после поездки в Турцию у меня никто не спрашивал у меня как с экстремизмом в Турции.

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

Сейчас будет немного неаппетитно, простите, но я постараюсь кратенько. На двух фотографиях выше — унитазы в Турции, один в пятизвёздочном отеле, другой — в обычной заправке на дороге из Антальи в Кемер. У них есть кое-что общее. В каждом туалете Турции, есть специализированный источник воды для подмывания попы. Так принято в исламе. Причём, подмываться требуется левой рукой, поэтому шланг в туалете на заправке идёт справа.

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

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

35 комментариев

«Fire in the hole»

Сейчас в сериале «4исла» («Numb3rs») фразу «fire in the hole» (букв. «огонь в дыру») перевели как «стреляй в окно». Забавно, вероятно переводчики не в курсе что это значит, поэтому перевели как смогли.

По сюжету взрывали дверь грузовика подрывным зарядом (двадцатая серия второго сезона), а «fire in the hole» означает что-то вроде «поберегись, сейчас рванёт». Обычно эту фразу кричат военные или, скажем, горняки перед взрывом.

Происхождение фразы достаточно ясно: когда-то порох в пушках поджигался через небольшое отверстие в стволе. «Fire in the hole» — команда канониру сунуть в отверстие факел и, заодно, предупреждение всем окружающим, что «сейчас бабахнет», что надо держаться в стороне от направления выстрела.

8 комментариев

Терри Пратчетт «Я надену чёрное»

На форуме сайта «Цитадель Детей Цвета» перевели книгу Терри Пратчетта «Я надену чёрное» («I shall wear midnight»).

Скачать можно (в формате DOC, упакован ZIP) с iFolder, Zalil, Fayloobmennik, TempFile, LetitFile, DiPi и GetZilla.

8 комментариев

CSSO

Ребята из симферопольского «Яндекса» сказали новое слово в оптимизации CSS — CSSO. Оптимизатор интересен тем, что умеет минимизировать с изменением структуры — то есть удалять перекрываемые свойства, производить слияние блоков с одинаковыми селекторами и тому подобное.

Сергей Крыжановский, который занимается программированием CSSO, в своём блоге хорошо описал отличия CSSO от YUI Compressor, CSSTidy и им подобных.

Например, CSSO умеет вот такое:

/* до CSSO */
.a {
    color: red;
    border: 0;
}

.b {
    color: red;
}

/* после CSSO */
.a{border:0}.a,.b{color:red}

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

42 комментария

Точка WiFi в аэропорту «Шереметьево»

Вернулся из Казани, сишу в Шереметьеве, в терминале «Д», жду такси. Смешно — для того, чтобы попасть в бесплатную WiFi-сеть «Terminal-D Free WiFi» пришлось кое-что поправить в HTML-исходнике страницы, которая показывается при подключении к точке (там надо нажать на кнопку «Next», чтобы попасть-таки в сеть) — заменил метод «POST» в форме на «GET», иначе сервер выдавал ошибку 405 («Method not allowed»).

Я попробовал в «Опере» и «FireFox», в обоих та же ошибка.

Может кто-то из читателей пользовался этой точкой? Тут всегда так или временный глюк какой-то?

8 комментариев

Ну не повар я

Жена утверждает, что во мне где-то зарыт кулинарный талант. Сегодня я определённо копал в каком-то неверном месте. Магазинную пиццу я умудрился приготовить так, что сверху она чёрная, а снизу белое тесто. Тешу себя надеждой, что где-то в середине приготовилось всё как надо, иначе меня ждёт верная голодная смерть.

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

Надеюсь, теперь она верит, что мой единственный кулинарный талант — заказ готовой еды.

15 комментариев

Как мне на «Мак ОС»

После перехода на «Мак» многие ребята спрашивают как мне. В том числе подходят люди, которые в скором будущем хотят сменить платформу.

Последним совет: тщательно подумайте зачем вам это.

На «Маке» всё как везде: за прошедшую неделю «Мак» у меня завис наглухо (пришлось выключить и включить) три раза. Я осознаю, что у меня весьма специфическая настройка — он введён в домен (имеется ввиду виндовый домен) и я часто хожу в интернет по VPN-туннелю. Тем не менее, факт остаётся фактом, иногда система висит.

Бывает, что «макось» зависает некритично (не навсегда), но неприятно. У меня это чаще всего это проверка пароля администратора. Из-за того, что моя машина в домене, операционная система пытается опросить домен и проверить пароль там, а домен не всегда доступен (нет WiFi или не включен VPN), из-за этого пароль проверяется иногда минутами. Во время проверки пароля делать ничего нельзя — система не даёт.

Приходится искать программы. Счастье, что у нас на работе очень много «маководов», они часто что-то могут подсказать. На «Маке» есть несколько программ, которых мне нехватало под «Виндоуз» (яркий пример — Sparrow, который показывает почту как instant messenger), но чаще ситуация обратная, подходящие программы найти трудно. Не в последнюю очередь это происходит из-за того, что под Mac OS очень много качественных платных программ, но мало бесплатных, пусть даже и некачественных. А для меня слово «подходящий» включает так же понятие «бесплатный».

С другой стороны, некоторые качественные программы стоят до смешного мало. Sparrow я купил, попользовавшись неделю, за $9,99.

У меня последняя модель MacBook Air (как говорят владельцы автомобилей «модель этого года»), спрашивали сильно ли она греется. Не сильно, мой Dell E4200 грелся сильнее. Ещё один важный вопрос — на сколько хватает аккумулятора. Я пока не понял, но судя по всему, это чрезвычайно сильно зависит от сценария использования. Вот сейчас со включенным WiFi и половиной яркостью экрана система мне посчитала, что аккумулятора хватит примерно на два с половиной часа.

На вопросы сколько памяти, какой процессор, какая видеокарта и прочие по «железу» я ответить не могу, просто не интересовался, для меня это совсем не важно и не знаю где это посмотреть, когда спрашивают.

Переучивание у меня прошло быстро. К основным комбинациям я привык примерно за день, остальные учу по мере надобности. Ничего в этом удивительного или сложного нет, многие играют в «World of Warcraft» или что-то похожее, вспомните насколько быстро вы привыкли к комбинациям клавиш, расположениям менюшек и индикаторов. Тут то же. Мне пришлось привыкать чуть-чуть меньше, из-за того, что я установил типографскую раскладку Бирмана, а там некоторые клавиши расположены «по-виндовому».

Удобно ли мне. Лично мне — удобно. Меня уже не смущает, что запуск программы из Finder это не «Enter» («Enter» это переименование), а ⌘+↓. Такого в системе много.

Я никогда не смогу привыкнуть, что программы нельзя безнаказанно убрать из Dock («панель задач» в терминах «Виндоуз»). Например, Adium (это единственный удобоваримый instant messenger) меня очень раздражает тем, что вертит спиннером при попытке соединения. Интранет-джаббер не всегда доступен (когда не включен VPN), в этом случае спиннер крутится бесконечно, это неприятно.

Мне много советуют программ, заменяющих стандартную функциональность Finder, Spotlight и прочего. Я пока ничего из этого пробовать не хочу (правда, я заменил «Терминал»), чтобы почувствовать систему. Пока я не прочувствовал все достоинства и недостатки, очень легко привить себе какие-то плохие практики, перепутав непривычное с плохим.

Если у вас есть ещё какие-то вопросы о том как мне эту неделю живётся на Mac OS X, спрашивайте, я отвечу.

27 комментариев

Как появился первый отечественный «Спектрум»

«Спектрум» (ZX Spectrum) — это компьютер, с которым связано три года моей жизни, несмотря на то, что настальгии по нему давно нет, всё равно иногда интересно узнать что-то новое о нём. В шестом номере газеты «Optron» за 10 января 1998 года была, оказывается, такая статья «Так, кто же первый?», о том как появился первый отечественный клон «Спектрума».

Статья интересна, помимо самой истории, описанием (хоть и без деталей) как вообще клонировалась тогда такая техника. Например:

Был такой Женя, где-то он имел контакты. Кто-то из иностранных студентов привез фирменный Спектрум сюда, в ОКБ. Этот Женя контактировал с Натоптой Евгением Евгеньевичем, который, увидев этот компьютер, попросил Женю взять его себе домой поиграться… Мы берем с работы 99-й осциллограф, тащим его туда и буквально проходим по всем ножкам, зарисовываем осциллограммы этой ULA’шки. Кроме того, у нас была информация по структуре программного обеспечения: буквально был исписан один листок.

Еще мы считали информацию из ROM компьютера. И начали работать. Евгений Евгеньевич Натопта занимался программным обеспечением, а  я, молодой еще тогда специалист, — конкретно железом. Собственно разработкой. По осцилограммам восстанавливал схему. (…)

Это было недолго. Месяц максимум.

Первый отечественный клон появился во Львове, а «разломали» его в ОКБ Львовского Политехнического института.

9 комментариев

«Крыть органами»

Узнал новое для себя выражение: «крыть о́рганами».

8 комментариев

Жест «сколько времени»?

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

Уже потом, когда я ответил на вопрос и пошёл дальше, я подумал — часы уже мало кто носит, а жест остался. Интересно, сколько пройдёт времени, прежде чем он превратиться в чистый символ, совершенно оторванный от ношения часов на руке?

24 комментария

CSS и JS в одном файле

Раз уж я затронул тему снижения числа коннектов к серверу при загрузке сайта, то было бы уместо вспомнить незаслуженно забытую технику — объединение JS и CSS в одном файле.

Насколько я знаю, первым эту технику исследовал некий ShivaP в статье «Combine CSS with JS and make it into a single download!» ещё четыре года назад.

Идея такова: в файле специальными ухищрениями помещается и код JavaScript, и код CSS, файл подключается два раза — один раз через тег SCRIPT, второй раз через тег LINK (как CSS), браузер грузит файл один раз, а второй раз берёт его уже из кеша.

Автор проверил свою идею на двух браузерах — IE и FireFox, на том и остановился.

Я проверил эту идею на гораздо бо́льшем числе браузеров (обнаружил проблемы на некоторых «Хромах» и исправил), а так же немного её видоизменил, разрешив использовать многострочный CSS без особых неудобств. Последнее нужно персонально мне, так как у меня нет возможности прогонять свой CSS через оптимизаторы и вытягивать таким образом код CSS в одну строку, а я хочу использовать эту технику у себя на сайте.

У техники есть ограничение, которое не портит много крови, но о нём необходимо знать — в тексте CSS и JS не должна встречаться конструкция «*/». Если в CSS достаточно просто выкусить все комментарии («*/» там, можно считать, нигде больше встретиться не может), то в JS за этим придётся следить, так как такая комбинация вполне может встретиться, в частности, в регулярных выражениях. Например: var regexp = /smth*/g;

В таких случаях звёздочку необходимо прятать (например, заменять её в регулярных выражениях на \x2A или разбивать строку: «smth*» + «/»).

Как ShivaP, первый исследователь этой техники, решил проблему размещения JS и CSS в одном файле можно почитать в оринальной статье, я же поступил несколько иначе, но сходным образом. Конструкции размещаются так, чтобы JS оказывался в комментариях CSS и наоборот. Я похожими вещами уже занимался неоднократно.

Мой код выглядит следующим образом (я поставил рядом две картинки, чтобы можно было сравнить как один и тот же код воспринимается интерпретатором JavaScript и парсером CSS):

JS и CSS (15.58КиБ)

Как видите, принцип довольно несложен.

Теперь несколько слов о том как это всё подключается:

<link type="text/css" rel="stylesheet" href="core.jscss" />

<script type="text/javascript" src="core.jscss#2"></script>

Обратите внимание на «#2» после core.jscss, этот хак позволяет обойти странное поведение некоторых версий «Хрома» — без него браузер не делал попыток подгрузить файл ещё раз и JavaScript не выполнялся.

Немаловажно так же, что заголовок «content-type» должен быть выставлен в «*/*», чтобы избежать проблем с FireFox 2.

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

Читать дальше

<?
ob_start();
$dir     = 'media/';
$expires = 10 * 365 * 24 * 60 * 60; // 10 years

$files = explode(',', str_replace('..', '', $_SERVER['QUERY_STRING']));
$css = $js = array();

foreach ($files as $file) {
   switch (pathinfo($file,  PATHINFO_EXTENSION)) {
       case 'css':
          $css[] = $dir . $file;
          break;

       case 'js':
          $js[] = $dir . $file;
          break;
    }
}

$css = @array_map('file_get_contents', array_unique($css));
$js  = @array_map('file_get_contents', array_unique($js));

$css = implode($css);
$js  = implode($js, ';');

// removes /* comments */ from css
$css = preg_replace('!/\*.*?\*/!s', '', $css);

// removes only first /* comment */ from js
$js  = preg_replace('!^/\*.*?\*/!s', '', $js);

echo <<<JSCSS
// /*
$js
"*/{"/*"}
$css
*/
JSCSS;

$len  = ob_get_length();
$txt  = ob_get_clean();
$etag = '"' . sha1($etag) . '"';

header("Content-type: */*; charset=utf-8");
header("Expires: " . gmdate('D, d M Y H:i:s \G\M\T', $_SERVER['REQUEST_TIME'] + $expires));
header("Cache-control: max-age=$expires, public");
header("Content-length: " . $len);
header('ETag: ' . $etag);

if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
    list ($hash) = explode(';', $_SERVER['HTTP_IF_NONE_MATCH']);

    if ($hash == $etag) {
        header('HTTP/1.1 304 Not modified, thanx for yr question');
        exit;
    }
}

echo $txt;

Формат вызова простой: скрипту через запятую перечисляются файлы, которые нужно объединить и подключить (файлы берутся из папки «media», см. переменную «$dir»):

/util/js+css.php?core.js,core.css,another.css,another.js

35 комментариев

Москва ✈ Казань ✈ Москва

Две ближайшие поездки в Казань: с 21-го по 24-е апреля лечу один на SQA Days-9, с 6-го по 10-е мая летим вместе с женой, повидаться с родными, друзьями.

6 комментариев

Увеличение числа параллельных соединений браузера к одному домену из HTML

Большинство разработчиков сайтов уже знают, что число соединений к серверу у браузеров лимитированно. Старые версии браузеров используют только два соединения на сервер, более свежие побольше (например, «Опера» 11.10 — аж 16).

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

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

Если сайт и канал вполне выдержат и большее количество соединений от одного пользователя (с учётом посещаемости), то есть хитрость, которую часто используют — загружать статику (картинки, файлы стилей и прочее) с других доменов. Их не обязательно располагать на других серверах, достаточно дать одному серверу несколько имён и использовать их для загрузки.

Выглядеть это может так: static1.example.org, static2.example.org и так далее. «Яндекс», к примеру, грузит свои данные с домена yandex.st. Как видите, подходит другой домен любого уровня.

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

Ничего страшного, чаще всего у пользователя такого хостинга аж четыре домена, просто мало кто об этом подозревает. Первые два достаточно очевидны — хостер часто, кроме какого-нибудь example.org, заводит ещё и www.example.org. Но ведь это два разных домена!

Ещё два не так очевидны— это домены «example.org.» и «www.example.org.». Обратите внимание на точку в конце домена.

Мои эксперименты (проверял на сервисе BrowserShots, операционные системы Linux, Windows XP, Mac OS X) говорят о том, что браузеры умеют с ними работать, то есть картинки с URL, где имя домена кончалось на точку, грузились нормально.

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

В эксперименте я выставил максимальное количество соединений к серверу в единицу («Опера» и «FireFox» позволяют это сделать, где это делается в браузерах на WebKit я не знаю, подскажите, если знаете) и создал две HTML-страницы: одну, где две картинки из моего скрипта грузятся с одного домена, вторую, где одна из них грузится с домена с точкой.

Варианты загрузки (49.40КиБ)

На скриншоте хорошо заметен результат и видно где какой эксперимент (по колонке «Домен»). По скриншоту видно, что я использовал хорошо всем знакомый FireBug, но на Dragonfly из «Оперы» картина такая же.

26 комментариев