программирование

Как обойти баг со сложными селекторами CSS3 в «Вебките»

В «Вебките» (а значит в «Хроме» и «Сафари») есть баг с поддержкой сложных селекторов CSS3. Насколько мне рассказывали, это даже не баг, а сознательное решение. Относится он к сложным селекторам, связанным с «:target», «:checked» и ещё некоторым. Смысл в том, что у браузера как будто нет внутренних проверок на то, что условия поменялись (изменился элемент, на который должен указывать «:target», например) и надо переприменить селекторы к элементам.

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

section {
    -webkit-animation: 0.1s hack infinite;
}
            
@-webkit-keyframes hack {
    from {margin: 0; padding: 0;}
    to {margin: 0; padding: 0;}
}

Теперь в теге «section» эти сложные селекторы будут срабатывать.

Оставить комментарий

ПХП и Винда

Порядка полугода назад разговаривали с кем-то о кроссплатформенности скриптовых языков, речь и о ПХП шла, в частности. Я, памятуя о каких-то проблемах, связанных с тем, что когда-то разрабатывал на собственной машине под «Виндой», а потом переносил на «Линукс», доказывал, что по меньшей мере в ПХП всё плохо, но конкретных проблем вспомнить не мог.

А тут, на днях, нужно было продумать веб-сервис на ПХП, который именно под «Виндоуз» и работает, нужно обращаться к КОМ-объекту программы, которая только на этой платформе бывает. Сразу проблема всплыла.

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

Функция не заработала правильно ни с «Апачем», ни с «Энжиникс», в комментариях пользователей к документации есть одинокий комментарий 2002-го года, который сообщает о проблеме:

Note that register_shutdown_function() does not work under Apache on Windows platforms. Your shutdown function will be called, but the connection will not close until the processing is complete. Zend tells me that this is due to a difference between Apache for *nix and Apache for Windows.

Похоже с тех пор ничего не изменилось. Ну хорошо, я вспомнил, что в последние ПХП входит патч php-fpm, который похожий функционал содержит — функцию fastcgi_finish_request. Оказалось, что php-fpm не работает под «Виндой».

Решили не перебирать больше решения, а сразу перейти на запуск отдельного фонового процесса. Оказалось и это не тривиально сделать. Под «Виндой» нужно это делать следующим образом:

$commandline = 'start /b тут-программа-которую-надо-запустить';
pclose(popen($commandline, 'r'));

Сплошные приключения.

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

%u → UTF-8

Столкнулся сегодня с задачей — надо сконвертировать пришедшую из JavaScript строку (в ней символы Юникода в виде «%uXXXX») в UTF-8 на PHP. Придумал, на мой взгляд, самый короткий способ это сделать:

echo json_decode(str_replace('%u', '\\u', json_encode($str_from_javascript)));

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

ChinesePython

А вы знали, что существует ChinesePython? Выглядит как настоящий, но не радует.

Язык полностью переведён на китайский, включая ключевые слова, встроенные типы и так далее, код основан на Пайтоне 2.1.3. Вот пример того как смотрится программа на этом языке:

載入 系統
文件名 = 系統.參數[1:]

定義 修正行尾(文件):
    內文 = 打開(文件名).讀入()
    內文 = 內文.替換('\n\r','\n')
    傳回 內文

取 文件 自 文件名:
    寫 修正行尾(文件)

«載入 系統» — это «import sys». Эта же программа на традиционном Пайтоне выглядит вот так:

import sys
filenames = sys.argv[1:]

def fixline(filename):
    text = open(filename).read()
    text = text.replace('\n\r','\n')
    return text

for file in filenames:
    print fixline(file)

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

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

Ерунда на itertools и генераторах

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

Прочитал тут на «Хабре», что дробь 1/998001 даёт последовательность из чисел от нуля до 997. Вот я и написал код с использованием генераторов и itertools, который берёт эту дробь и выводит из неё все числа последовательно, пока не попадутся числа с разницей не равной единице.

from decimal import Decimal, getcontext
from itertools import groupby, islice, izip, takewhile, chain, imap

getcontext().prec = 3000

n = (
    groupby(
        enumerate(
            islice(str(1 / Decimal(998001)), 2, None)
        ),
        lambda n: n[0] // 3)
    )

n = (int(''.join(n[1] for n in n[1])) for n in n)

n = chain(*takewhile(lambda n: n[1] - n[0] == 1, izip(n, n)))

print ', '.join(imap(str, n))

Кстати, для вычисления дроби с точностью до трёхтысячного знака, используется модуль decimal.

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

from decimal import Decimal, getcontext

getcontext().prec = 3000

number, prev, out = str(1 / Decimal(998001)), -1, []

for i in xrange(2, len(number), 3):
    curr = int(number[i:i+3])

    if curr - prev != 1: break

    prev = curr
    out.append(str(curr))

print ', '.join(out)

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

Некоторые проблемы SQLite

Товарищ Адепт пишет в ЖЖ про проблемы SQLite. Если вкратце, то выявлены минимум две проблемы.

SQLite при снятии дампа «CREATE VIEW» выписываются в порядке создания. Если у нас две вьшки, «A» и «Б», созданные в порядке перечисления и мы пересоздаём вьюху «А» с использованием «Б», то при заливке дампа будет ошибка — в дампе-то первым будет создание вьюшки «А», которая попытается использовать несуществующую вью «Б».

Если создана вью, в конце которой комментарий стоит, то и в дампе будет комментарий в конце строки создания, экранирующий конечную точку с запятой.

create view commented_view
  as select something
       from some_table -- pretty useless view;
create view another_view as…

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

Стеганография и пробел нулевой длины

Болею, не спится мне. Вспомнил старую свою идею.

В Юникоде есть такой символ замечательный — пробел нулевой длины (код 0x200B), на печать не выводится, понимается всеми современными браузерами и большинством редакторов. Интервала между буквами, как следует из названия, не даёт.

Идея простая, позволяет прятать в текст другой текст, так, чтобы первый не менялся, а второй всегда копировался вместе с первым. Основная мысль — пользуясь буквами исходного текста, как разделителем, ставим между ними столько пробелов нулевой длины, чтобы их число равнялось коду скрываемого символа.

Например. Дан текст: «Болк», в нём надо скрыть короткий текст: «yes». Я взял английские символы, чтобы не заморачиватсья с кодировкой.

Коды символов «yes» — 121, 101, 115. Значит текст приобретает следующий вид:

[121 символ пробела нулевой длины]Б[101 символ пробела нулевой длины]о[115 символов пробела нулевой длины]лк

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

​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​#​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​E​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​x​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​a​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​m​p​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​l​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​e​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​b​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​y​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​E​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​v​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​g​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​e​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​n​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​y Stepanischev
from itertools import groupby, izip_longest
import sys
import codecs

sys.stdin = codecs.getreader('utf-8')(sys.stdin)
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

toenc = 'Evgeny Stepanischev'
input = sys.stdin.read()

def decode(input):
    return ''.join(chr(31+len(list(x[1])))
            for x in groupby(input, lambda x: x == u"\u200b") if x[0])

def encode(input):
    if len(input) < len(toenc):
        raise ValueError()

    return ''.join(x[0] + x[1]
            for x in izip_longest((u"\u200b" * (ord(x)-31)
            for x in toenc), input, fillvalue=''))


print(encode(input) if input.find(u"\u200b") == -1 else decode(input))

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

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

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

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

О закладках в БИОСе

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

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

CSS hack: firefox 3.5+

Для «Файерфокса» есть простой, не то чтобы даже CSS-хак, а просто метод «обнулить» свойство специальным значением, если именно для этого браузера это и требуется. В CSS3 для этого придумано значение «initial», которое «Файерфокс» поддерживает должным образом с версии 3.5 (для некоторых свойств это значение поддерживается аж с первой версии). Значение поддерживается с префиксом, что и позволяет его использовать как своеобразный CSS-хак:

body {
    background-color: red;
    background-color: -moz-initial; /* для FF — значение свойства по-умолчанию */
}

.blk {
    display: none; /* для всех браузеров блок скрыт, */
    display: -moz-initial; /* кроме FF */
}

Браузеры на основе «Вебкит» так же поддерживают это значение (со 125 сборки), правда безо всякого префикса.

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

С Новым годом, что ли?

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

[][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+
!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+
([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+
!![])]+([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])
[+!![]]][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+
([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+
([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])[+!![]]]
(([]+!![])[+!![]]+([]+{})[-~[]<<[-~[]+-~[]]]+
([]+!![])[-[]]+([]+[][{}])[-[]]+([]+!![])[+!![]]+
([]+[][{}])[-~[]]+([]+{})[~[]+(+!![]<<!![]+!![]+
!![])]+([]+![])[!![]+!![]+!![]]+([]+![])[!![]<<!![]+
!![]]+([]+![])[!![]+!![]]+([]+![])[-[]])()
[([{}-{}]+[])[-~[]]+([]+![])[!![]+!![]]+([]+![])
[!![]<<!![]+!![]]+([]+!![])[-~[]]+([]+!![])[+[]]](
[][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+
!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+
([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+
([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])[+!![]]]
[([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+
([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+
([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+
([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])[+!![]]]
(([]+!![])[+!![]]+([]+{})[-~[]<<[-~[]+-~[]]]+
([]+!![])[-[]]+([]+[][{}])[-[]]+([]+!![])
[+!![]]+([]+[][{}])[-~[]]+([]+{})[~[]+(+!![]<<!![]+
!![]+!![])]+([]+![])[!![]+!![]+!![]]+([]+![])
[!![]<<!![]+!![]]+([]+![])[!![]+!![]]+([]+![])
[-[]])()[([]+![])[-~[]]+([]+!![])[+![]]+([]+{})
[-~[]]+([]+{})[!![]+!![]]](([]+![])[-~[]]+
([][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+
([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])
[+![]]+([]+!![])[+!![]]+([]+!![])[-[~[]+~[]]]+
([]+{})[-~[]+(!![]<<!![]+!![])]+([]+!![])[+![]]+
([]+{})[-~[]]+ ([]+!![])[+!![]]]+[])[(-~[]+[+![]])-!![]])+
([]+![])[-~[]]+
(/!/[([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+([]+[][{}])
[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})
[-~[]]+([]+!![])[+!![]]]+[])[+!![]+[!![]<<!![]-~[]]]+
(/!/[([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+([]+[][{}])
[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})
[-~[]]+([]+!![])[+!![]]]+[])[+!![]+[!![]<<!![]-~[]]]+
([]+[~[]/[]])[[[!![]+!![]]*[!![]+!![]]]*[!![]+!![]]]+
([]+{})[~[]+(+!![]<<!![]+!![]+!![])]+
([]+[][{}])[-~[]]+
([]+{})[-~[]<<[-~[]+-~[]]]+
([][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])
[+![]]+([]+!![])[+!![]]+([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+
(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])
[+!![]]][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+
([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])[+![]]+
([]+!![])[+!![]]+([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+
(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])
[+!![]]](([]+!![])[+!![]]+([]+{})[-~[]<<[-~[]+-~[]]]+
([]+!![])[-[]]+([]+[][{}])[-[]]+([]+!![])[+!![]]+([]+[][{}])
[-~[]]+([]+{})[~[]+(+!![]<<!![]+!![]+!![])]+([]+![])
[!![]+!![]+!![]]+([]+![])[!![]<<!![]+!![]]+([]+![])
[!![]+!![]]+([]+![])[-[]])()+[])[([]+![])[!![]+!![]+
!![]]+([]+![])[!![]+!![]]+([]+[][{}])[!![]+(!![]<<!![]+!![])]+
([]+{})[!![]+(!![]<<!![]+!![])]+([]+!![])[!![]+!![]+!![]]](~[]+~[])[-[]]+
([]+{})[~[]+(+!![]<<!![]+!![]+!![])]+
(~[]/[]+[])[-~[]<<-(~[]+~[]+~[])]+
([~[]<[]]+[])[-[~[]+~[]+~[]]]+
([{}-{}]+[])[-~[]]+
 ([]+!![])[+!![]]+
(/!/+[])[-~[]])

Запускать лучше всего в консоли браузера (в Firebug, Dragonfly и так далее). Цели сократить код у меня не было, более того, кое-где для разнообразия (буквы-то повторяются) я придумывал разные конструкции, чтобы не так скучной было. С Новым годом!

Если кого-то поразило это буйство скобочек, то намекаю ещё раз — это полноценная программа на Яваскрипте.

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

Обработка данных интернет-клиента «Альфа-банка»

Накидал небольшую программу для обработки данных из интернет-клиента «Альфа-банка». Первым параметром указывается CSV-файл, выгруженный из клиента, на выходе три столбца — дата операции, сумма и описание.

Программа на Пайтоне:

# coding: utf-8
import sys
import re
from datetime import date
from itertools import islice

output = []

with open(sys.argv[1], 'r') as f:
    for line in islice(f, 1, None):
        n, detail, arr, flow = line.decode('cp1251').split(';')[4:8]

        if n[:4] == 'BERR' or n[:6] == 'CASHIN': continue
        flow = float(flow.replace(',', '.'))

        if flow > 0:
            dates = re.findall(r'\d{2}\.\d{2}\.\d{2}', detail, re.UNICODE)

            if dates:
                d = min(date(*(int(x) for x in d.split('.')[::-1])) for d in dates)
                output.append({
                    'detail': detail,
                    'flow': flow,
                    'date': d,
                })

for k in sorted(output, lambda x, y: cmp(x['date'], y['date'])):
    data = zip(('y', 'm', 'd',), k['date'].timetuple()[:3]) + k.items()

    print "%(d)02d.%(m)02d.%(y)02d %(flow)09s %(detail)s" % dict(data)

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

Библиотеки Джаваскрипта на сторонних сайтах

Наверное, большинство почувствовало вчера, что большинство сайтов «Гугла» были неработоспособны. Не знаю с чем там были связаны проблемы, мне и не интересно, интересно другое — не работали и некоторые сайты, которые сервисами «Гугла» не являются. Например, мой любмый «Стековерфлоу».

Догадываетесь почему?

Загрузка «Стековерфлоу» (51.95КиБ)

Догадываетесь, наверное, по картинке видно, да и заголовок подсказывает. «Стековерфлоу» грузит библиотеку jQuery со специального сервиса «Гугла», вся разгадка.

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

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

Почему надо хорошо знать свой инструмент

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

Ан нет, отличная презентация Олве Маудала и Джона Джаггера «Deep C (and C++)» даёт надежду, что я не последний и хорошо показывает различия между посредственным знанием и хорошим.

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

Первый язык программирования высокого уровня — Планкалкюль ★

Планкалкюль (Plankalkül) — первый в мире язык программирования высокого уровня, разработанный немецким инженером Конрадом Цузе между 1942 и 1946 годами для его компьютера «Z4».

Экспонат в музее: деревянный пульт управления с клавиатурой и панелью приборов, застеклённый, рядом — высокие шкафы с электронными компонентами.
Компьютер «Z4», фото взято из «Википедии»

Шла Вторая Мировая Война, Цузе работал в отрыве от учёных других стран, совершенно самостоятельно. За это время он создал не только язык программирования, но и написал на нём 49 страниц программ для оценки шахматных позиций. Полностью его работа была опубликована много позже, в 1972.

Только в 1957 году (работы были начаты в 1954) появился Фортран — язык, который большинство считает первым языком высокого уровня.

«Z4» был электро-механическим, поэтому компилятора или интерпретатора Планкалкюля не существовало, но в 2000 году в Свободном универсистете Берлина (Freie Universität Berlin) был создан интерпретатор его диалекта (запускается в виде Ява-аплета в браузере).

Диалект, который реализуется, назван Plankalkül-2000 и отличается упрощённой формой записи программ. Например, упрощена форма записи, Цузе использовал двухмерную запись — первой строкой записывалось само выражение, а ниже некоторые его аргументы (тип переменной, индексы и так далее). Кроме того, были упрощены и значки самих операций, приведены к более привычному нам с вами виду.

Язык довольно богатый, на мой взгляд, для сороковых годов-то: есть условные конструкции, два вида циклов (аналог while и for), есть массивы и кортежи, можно описывать и вызывать подпрограммы (но рекурсии нет).

Все переменные делятся на четыре вида.

  • Variablen (Входные переменные) — это входные переменные подпрограмм, доступны только для чтения начинаются с буквы V и номера;
  • Zwischenwert (Значения промежуточные), доступны для чтения и записи, предназначены для хранения промежуточных вычисляемых значения, начинаются с Z и номера;
  • Resultatwerte (Результат) — в этих переменных возвращается результат вычисления, начинаются с R и номера;
  • Indizes (Индексы) — переменные цикла (аналога for), начинаются с i, дальше может быть номер, номер необходим для организации вложенных циклов.

Доступны три типа переменных. Несмотря на то, что «Z4» умел оперировать числами с плавающей точкой, интерпретатор этого не умеет.

Для целого неотрицательного указывается размерность в битах. Есть две формы записи 0 — один бит, n.0 — «n» бит, например, 8.0 — один байт (8 бит). Кортеж указывается в скобках, например (3.0, 4.0) — это две переменные в три и четыре бита, кортеж должен иметь более одного элемента. Массив записывается через точку, к примеру: 4.5.0 — массив из четырёх элементов по пять бит в каждом, 32.(0, 8.0, 16.0) — 32 кортежа, в каждом из которых три переменных: один бит, восемь и шестнадцать.

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

Одна из них вычисляет указанное (по порядку) число Фибоначчи:

P0 FactEvgenyStepanischev (V0[:4.0]) => (R0[:10.0])
(0, 1) => (Z0[:10.0], Z1[:10.0])

W1 (V0[:4.0]) [
    i > 0 -> (Z0[:10.0] + Z1[:10.0], Z1[:10.0] - Z0[:10.0]) => (Z1[:10.0], Z0[:10.0])
]

Z1[:10.0] => R0[:10.0]

END

Запускать следует так: открываете страницу с интерпретатором, копируете в окно мою программу, нажимаете «Compile», открывается отдельное окно с Ява-аплетом (требует, чтобы на компьютере была установлена Ява), в открывшемся окне мышкой побитно набираете начальное значение V0 (нажимать надо на зелёные кружки́), потом в окне браузера нажимаете «Run», в красной строчке (R0) увидите получившееся значение.

Скриншот интерфейса «Plan P0» с рядами зелёных, синих и красных фигур (круги, ромбы) и цифрами 13, 253, 233.
Запущенная программа на Планкалкюле

Подпрограммы в языке начинаются с символа P и уникального номера, дальше идёт имя, по которому её можно будет вызвать, у меня подпрограмма называется FactEvgenyStepanischev, заканчивается подпрограмма ключевым словом END (в оригинальном Планкалкюле его не было).

У подпрограммы описываются принимаемые и возвращаемые значения, у меня используется одна переменная на вход, размерностью 4 бита и одна на выход, размерность в 10. Первой строкой присваиваются значения «ноль» и «один» промежуточным переменным Z0 и Z1. Тип переменных приходится указывать при каждом их использовании, преобразовать переменную в другой тип нельзя.

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

Операция «стрелка» (->) — условная конструкция, часть справа будет выполнена, если выражение слева истинно. В диалекте работают только самые простые выражения, например цикл туда подставить можно, но кнопка «Run» в аплете у меня не появилась, поэтому я ограничился присваиванием внутри цикла.

Я тут использовал комплексное присваивание, которое хорошо знакомо тем, кто использует Перл, Пайтон или ПХП, но работает оно иначе — присваивания выполняются последовательно, слева направо, поэтому я не могу ограничиться,

(Z0[:10.0] + Z1[:10.0], Z1[:10.0]) => (Z1[:10.0], Z0[:10.0])

результат будет не тот, который ожидается.

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

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

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

Многозадачность в «Гоу»

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

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

package main

import (
    "io"
    "os"
    "crypto/sha512"
    "encoding/hex"
    "runtime"
    "strconv"
)

func main () {
    ch := make(chan string)

    N := 3

    runtime.GOMAXPROCS(N)

    for i := 0; i<N; i++ {
        go func (name string, num int, ch chan string) {
            if f, err := os.OpenFile(name, os.O_RDONLY, 0666); err == nil {
                defer f.Close()
                h := sha512.New()

                io.Copy(h, f)

                ch <- strconv.Itoa(num) + ". " + hex.EncodeToString(h.Sum())
            } else {
                panic("Cannot open file")
            }
        }(os.Args[0], i, ch)
    }

    for ; N > 0; N-- {
        print(<-ch, "\n")
    }
}

Что здесь происходит.

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

Дальше, очевидно, подключаются модули, в языке базовых функций почти нет, поэтому даже для такой небольшой программы мне понадобилось шесть модулей: «io» для копирования данных из одного потока в другой, «os» для работы с файлом, «crypto/sha512» для хеширования, «encoding/hex» для кодирования получившейся хеш-суммы в шестнадцатеричное представление, «runtime» чтобы выставить сколько го-программ будет выполняться параллельно и «strconv» чтобы сконвертировать число в строку.

Функция main — входная точка основного модуля программы, именно она (как в «Си») вызывается в самом начале.

Далее я создаю (make) небуферизированный «канал» и объявляю, что по нему будут идти строки, конструкция «:=» многим напоминает Паскаль, но отношения к нему не имеет, это синтаксический сахар для создания локальной переменной. «Канал» — это хорошее, говорящее название, хорошо отображающее суть, это действительно канал общения го-программы с чем-то извне.

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

Внутри го-программы открывается файл, он копируется во входной поток хеширующей функции, считается хеш, конвертируется в шестнадцатеричное значение и отправляется (конструкцией «ch <- smth») в «канал».

Цикл не ждёт выполнения го-программы, а просто запускает их в фоне.

Дальше идёт второй цикл, где N раз запускается команда на чтение из канала („… <- ch»).

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

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

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

Какой язык программирования изучать на перспективу

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

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

Над цифрами выше трудятся уже только оверклокеры, заливая штаны жидким азотом. Гонка частот уже, кажется, остановлена, началась гонка за количество ядер. Недавно был выпущен первый смартфон с четырьмя процессорами, а рабочая станция «Мак Про» поставляется в топовой конфигурацией с 12 процессорами. Сегодня на «Хабре» появилась новость о процессоре GA144, который будет продаваться за шестьсот рублей. У него 144 ядра.

Не говоря уже о GPU, у которых количество ядер приближается к тысячам.

Если взять какой-то язык, который легко умеет параллелиться, имеет более-менее привычный большинству (читай «си-подобный») синтаксис, компилятор или интерпретатор которого умеет использовать GPU в нужных местах (сортировки, поиск и прочее), то он может иметь успех со временем.

Причём «легко» не обязательно даже так, как это сделано в «Гоу» (хотя и желательно) — отдельной конструкцией языка (go), нет нужды сводить к этому всю идеологию языка, как в «Эрланге», просто чтобы это делалось, в тривиальных случаях, в одну-две строки, но было гибко управляемым в случае такой потребности. Нужно понижать порог вхождения в многозадачность, к чёрту подробности, в простейших случаях это должно делаться легко и просто, пусть и не особо эффективно.

Например, на «Пайтоне» как-то так:

@fork
def sort(arr):
    arr.sort()
    return arr

 # тут функция начала фоном работать
result = sort(somearray)

… много кода …

# а тут уже доступны результаты, если недоступны, то будем ждать
print([el for el in result if el])

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

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

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

«Здравствуй, мир» на Коболе

Я как-то проверял сколько занимает минимальная программа на «Гоу», оказалось — 232КБ. Посмотрел как дела с этим у Кобола. «ОупенКобол» скомпилировал мой helloword в девятикилобайтный запускаемый файл, что неплохо — сишный вариант у меня занял всего на килобайт меньше.

Вот так выглядит моя первая программа на Коболе:

000100*Моя первая программа на Коболе
000200 IDENTIFICATION DIVISION.
000300 PROGRAM-ID. hw.
000400 AUTHOR. Evgeny Stepanischev
000500 PROCEDURE DIVISION.
000600      DISPLAY "Привет, мир!".
000700      STOP RUN.

Первые шесть символов каждой строки — её номер, как легко догадаться. В современном Коболе их можно опустить (OpenCobol поддерживает этот синтаксис с ключом «-free»). Дальше идёт специальный символ, один из четырёх, насколько я знаю. Пробел — значит дальше начало новой строки кода, «звёздочка» — комментарий, минус — продолжение предыдущей строки кода (строки не должны быть длинее 80 символов) и «D» — некий «debug», для чего он, я пока не знаю.

В старом Коболе очень жёсткий порядок всего и вся. Мало того, что секции программы должны быть описано в строгом порядке, так ещё и столбец тоже имеет значение! То есть Кобол строго следит за отступами. Например, мой код с DISPLAY и STOP RUN начинается с 12-го символа. Так надо. А c 73 символа до 80-й можно писать что угодно — проигнорируется.

Язык очень многословный и имеет целую тучу зарезервированных слов, некоторые из которых весьма доставляют:

bolk@Bolk ~/Projects/Cobol  $ cobc --list-reserved | wc -l
     530

bolk@Bolk ~/Projects/Cobol  $ cobc --list-reserved | egrep '[[:digit:]]' | egrep -v 'UTF|UCS|FLOAT'
COMP-1				(Y)
COMP-2				(Y)
COMP-3				(Y)
COMP-4				(Y)
COMP-5				(Y)
COMPUTATIONAL-1			(Y)
COMPUTATIONAL-2			(Y)
COMPUTATIONAL-3			(Y)
COMPUTATIONAL-4			(Y)
COMPUTATIONAL-5			(Y)
STANDARD-1			(Y)
STANDARD-2			(Y)
SUB-QUEUE-1			(N)
SUB-QUEUE-2			(N)
SUB-QUEUE-3			(N)

Как я буду учить это всё великолепие, пока не знаю, не уверен, что интерес к языку у меня настолько глубок. Впрочем, может оказаться, что бо́льшая часть из указанных 530 зарезервированных слов нужны в жизни очень редко. А ещё, бегло пробежавшись по документации, я нашёл большое количество синонимов. За примерами далеко ходить не надо, «COMP-N» и «COMPUTATIONAL-N» из вышеприведённого листинга — синонимы.

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

Как заменить табы, используя sed

Утилита командной строки sed (это потоковый редактор), как-то неочевидно работает с символом табуляции (tab). Выражения «\t», «\x09», «\011» не срабатывают даже в расширенном режиме регулярных выражений (включается флагами „-r“ или „-E“, в зависимости от системы).

Я немного поэксперементировал, оказалось, что sed вполне воспринимает этот символ как таковой, а все «магические выражения» не работают. Поскольку «таб» имеет специальное значение в командной строке, ввести его можно либо через комбинацию Ctrl+V, tab, либо Ctrl+V, Ctrl+I. Во многих шеллах (например, в bash, ksh, sh) сработает специальный синтаксис с долларом, как в примере ниже.

Например, если вам надо заменить последовательные «табы» на один, то это может выглядеть вот так:

sed $'s/\t\t*/\t/g' sample.txt # именно \t\t*, а не \t+

# или

sed -E $'s/\t+/\t/g' sample.txt # в Линуксе ключ „-r“

Если вы знаете как в sed можно работать с tab проще, расскажите, пожалуйста, в комментариях.

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

Почему не следует расширять встроенные типы JavaScript

На Хабре хорошая статья — «Monkey-Patching, или Расширение Встроенных Типов: религия или осознанный выбор?».

Оставить комментарий

Зачем я учу языки программирования

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

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

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

Недавно я написал, что поставил себе Кобол на Мак, у многих возник вполне закономерный вопрос — зачем?

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

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

← 1 36 37 38 39 40 67 →