?

Log in

No account? Create an account

Я в интернете
zhtw
Я в соц. сетях. (В ЖЖ больше не пишу.)

UPD: Правильней начать с моего профиля на keybase.io.

Map-reduce
zhtw
Каждый раз, когда я с кем-то разговариваю на тему map-reduce, у меня складывается впечатление, что мы говорим на разных языках.

Поэтому я решил написать, что я понимаю под map-reduce, и, надеюсь, все, кто понимают под этим что-то другое, ткнут мне пальцем.


Итак, оператор map.

Вот один из вариантов его определения.

Даны множества X, и Y.

Map -- это оператор, отображающий пары (A, f) в B, где A ⊂ X, B ⊂ Y, f: X → Y, такие, что f(A) = B, а точнее B = { f(x) | x ∈ A }.

Пример 1. f(x) = 2x. map(f, {1, 2, 4}) = {2, 4, 8}.

Пример 2. f(x) = 3x mod 6, map(f, {3, 4, 0}) = { 3, 0 }.

Это лишь пример возможного определения. Когда map реалзуется в языках программирования, обычно делают некоторые отступления. Например, множества могут быть представлены в виде списков или массивов, и, раз уж массивы и списки могут содержать одинаковые элементы, и порядок играет роль, то тогда A и B -- кортежи с равным числом элементов из X и Y соответственно.

Классический вариант map в Лиспе для списков:


(define (map f l)
  (if (null? l)
    '()
    (cons (f (car l)) (map f (cdr l)))))


Или на Питоне для массивов (они же списки)


def map(f, l):
  return [f(x) for x in l]


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

Оператор reduce.

Даны множества X и Y. Reduce отображает тройку (x0, f, <x1, x1... xn>) в x, где

f: X×Y → X,
x, x0 ∈ X,
x1... xn ∈ Y, причем
x = f(...f(f(x0, x1), x2)...)

Пример 1.

f(a, b) = a + b
reduce(0, f, <1, 2, 3, 4, 5>) = 0 + 1 + 2 + 3 + 4 + 5 = 15.

Пример 2.
f(A, b) = A ∪ { b }.
reduce(∅, f, <1, 2, 3, 2, 4>) = { 1, 2, 3, 4 }.

Когда reduce реализуют в языках программирования, кортеж представляют в виде списка, массива или отложенной последовательности.
Классический вариант reduce на Лиспе для списков:


(def (reduce x0 f l)
  (if (null? l)
    x0
    (reduce (f x0 (car l)) f (cdr l)))


На Питоне:

def reduce(x0, f, l)
  res = x0
  for x in l:
    res = f(res, x)
  return res


Reduce очень универсальная операция. Через нее легко определить очень много операций. С первого взгляда неочевидно, но через reduce можно даже определить реверс списка.

Пример на Лиспе:


(def (reverse l)
  (reduce '() (lambda (a b) (cons b a)) l))


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

Map и reduce в сочетании дают очень мощный механизм написания алгоритмов работы с последовательностями.

На самом деле reduce -- это лишь одна из двух симметричных операция folding'а: rfold и lfold. Rfold -- это reduce, а lfold получается, если в f поменять аргументы местами, и определить x так:

x = f(x1, f(x2, ...f(xn, x0)...))

Но это не очень важно.

Так вот, map и reduce -- простейшие операции, известные миллион лет, определены в библиотеках всех известных языков программирования.

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

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

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

Различные реализации того, что сейчас называют распределенной моделью вычислений map-reduce, вводят и другие ограничения на элементы, чтобы предоставить всякие удобства для программиста. Например, кроме ключа можно еще ввести подключ, и предоставить возможность сортировки каждого из отрезков по подключу, перед тем, как выполнять reduce над ними. Map может принимать в качестве f многозначную функцию и пр.

Как бы то ни было. Чтобы иметь общее представление о том, какие алгоритмы «ложатся» на модель map-reduce, важно помнить что такое базоыве map и reduce.

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

Статистика по одному китайскому тексту
zhtw
Я взял рассказ 浪迹天涯话买卖 современной тайваньской писательницы 三毛. Посчитал статистику и получил вот что.
Текст содержит 3606 иероглифов (включая название), различных иероглифов всего 812, из них 463 составляют 90% текста. 336 иероглифов встречаются ровно один раз.


的 - 192 活 - 9 望 - 5 诞 - 3 难 - 2 放 - 2 闹 - 1 究 - 1 散 - 1 城 - 1
我 - 92 方 - 9 成 - 5 认 - 3 镑 - 2 摇 - 2 长 - 1 税 - 1 教 - 1 坏 - 1
是 - 91 很 - 9 情 - 5 表 - 3 错 - 2 推 - 2 镶 - 1 称 - 1 政 - 1 圈 - 1
了 - 66 常 - 9 怪 - 5 虚 - 3 酸 - 2 排 - 2 锅 - 1 科 - 1 收 - 1 图 - 1
不 - 59 头 - 9 度 - 5 荷 - 3 酱 - 2 掌 - 2 销 - 1 福 - 1 撩 - 1 园 - 1
在 - 45 吃 - 9 己 - 5 药 - 3 转 - 2 拿 - 2 银 - 1 票 - 1 撒 - 1 嘱 - 1
那 - 41 他 - 9 定 - 5 茫 - 3 跳 - 2 拍 - 2 钟 - 1 示 - 1 掏 - 1 嘛 - 1
来 - 41 事 - 9 孩 - 5 至 - 3 跟 - 2 把 - 2 醒 - 1 确 - 1 掉 - 1 啡 - 1
去 - 36 中 - 9 如 - 5 结 - 3 越 - 2 打 - 2 醉 - 1 研 - 1 据 - 1 啊 - 1
个 - 34 迷 - 8 夜 - 5 红 - 3 走 - 2 房 - 2 酒 - 1 石 - 1 捧 - 1 啃 - 1
货 - 33 进 - 8 回 - 5 累 - 3 贵 - 2 或 - 2 邀 - 1 睡 - 1 拮 - 1 哭 - 1
得 - 32 觉 - 8 品 - 5 等 - 3 贩 - 2 懂 - 2 遥 - 1 目 - 1 括 - 1 哈 - 1
大 - 32 街 - 8 向 - 5 笔 - 3 质 - 2 感 - 2 通 - 1 盘 - 1 拚 - 1 咖 - 1
公 - 31 真 - 8 同 - 5 竿 - 3 豆 - 2 怎 - 2 途 - 1 盖 - 1 拖 - 1 咐 - 1
百 - 30 日 - 8 变 - 5 竹 - 3 谜 - 2 忆 - 2 逐 - 1 疼 - 1 拒 - 1 味 - 1
有 - 28 年 - 8 其 - 5 童 - 3 调 - 2 微 - 2 透 - 1 疚 - 1 拉 - 1 周 - 1
时 - 28 实 - 8 倒 - 5 穿 - 3 话 - 2 征 - 2 选 - 1 甜 - 1 抽 - 1 吸 - 1
司 - 28 她 - 8 你 - 5 程 - 3 计 - 2 往 - 2 退 - 1 甘 - 1 抱 - 1 启 - 1
可 - 27 太 - 8 件 - 5 睛 - 3 言 - 2 彩 - 2 迹 - 1 瓶 - 1 抛 - 1 吧 - 1
人 - 26 卖 - 8 买 - 5 皮 - 3 解 - 2 弄 - 2 返 - 1 璃 - 1 扫 - 1 否 - 1
里 - 24 前 - 8 乐 - 5 白 - 3 袜 - 2 应 - 2 辈 - 1 珂 - 1 托 - 1 吗 - 1
这 - 24 再 - 8 马 - 4 痛 - 3 落 - 2 广 - 2 较 - 1 玻 - 1 慕 - 1 叹 - 1
生 - 23 间 - 7 需 - 4 班 - 3 菜 - 2 干 - 2 躲 - 1 玩 - 1 愿 - 1 另 - 1
小 - 23 课 - 7 门 - 4 牙 - 3 荣 - 2 带 - 2 踏 - 1 玉 - 1 惑 - 1 句 - 1
家 - 22 虽 - 7 铺 - 4 片 - 3 草 - 2 布 - 2 超 - 1 猜 - 1 惋 - 1 古 - 1
天 - 22 给 - 7 重 - 4 爱 - 3 节 - 2 已 - 2 赴 - 1 牌 - 1 惊 - 1 发 - 1
西 - 21 种 - 7 逛 - 4 满 - 3 脱 - 2 差 - 2 购 - 1 烟 - 1 悍 - 1 双 - 1
么 - 21 次 - 7 边 - 4 渴 - 3 职 - 2 将 - 2 贪 - 1 炼 - 1 息 - 1 友 - 1
要 - 20 杂 - 7 跑 - 4 流 - 3 者 - 2 寄 - 2 责 - 1 点 - 1 恨 - 1 及 - 1
也 - 20 本 - 7 起 - 4 沙 - 3 群 - 2 宝 - 2 负 - 1 激 - 1 性 - 1 压 - 1
都 - 19 月 - 7 费 - 4 氛 - 3 缘 - 2 娱 - 2 贝 - 1 滩 - 1 急 - 1 厉 - 1
美 - 19 最 - 7 记 - 4 楚 - 3 纹 - 2 姐 - 2 谓 - 1 湾 - 1 思 - 1 印 - 1
看 - 19 快 - 7 被 - 4 梦 - 3 纸 - 2 够 - 2 读 - 1 渺 - 1 怕 - 1 南 - 1
上 - 19 形 - 7 行 - 4 柏 - 3 繁 - 2 外 - 2 该 - 1 港 - 1 律 - 1 医 - 1
就 - 17 它 - 7 舍 - 4 条 - 3 糊 - 2 境 - 2 诗 - 1 渐 - 1 影 - 1 区 - 1
着 - 16 四 - 7 背 - 4 服 - 3 簿 - 2 堂 - 2 许 - 1 淋 - 1 归 - 1 力 - 1
以 - 16 叫 - 7 老 - 4 更 - 3 管 - 2 垃 - 2 讲 - 1 淇 - 1 强 - 1 副 - 1
然 - 15 只 - 7 缺 - 4 春 - 3 答 - 2 坐 - 2 让 - 1 涨 - 1 引 - 1 刷 - 1
好 - 15 做 - 7 绿 - 4 支 - 3 租 - 2 圾 - 2 角 - 1 涌 - 1 式 - 1 初 - 1
十 - 15 住 - 7 经 - 4 挤 - 3 秘 - 2 喷 - 2 观 - 1 海 - 1 异 - 1 凳 - 1
下 - 15 业 - 7 纯 - 4 拾 - 3 神 - 2 喳 - 2 襟 - 1 测 - 1 座 - 1 凤 - 1
金 - 14 香 - 6 算 - 4 报 - 3 社 - 2 喜 - 2 裘 - 1 泪 - 1 府 - 1 凡 - 1
说 - 14 衣 - 6 相 - 4 批 - 3 磨 - 2 喃 - 2 袖 - 1 法 - 1 庙 - 1 准 - 1
又 - 14 花 - 6 淡 - 4 才 - 3 碗 - 2 商 - 2 袍 - 1 沉 - 1 库 - 1 净 - 1
东 - 14 笑 - 6 校 - 4 手 - 3 破 - 2 唱 - 2 衬 - 1 汤 - 1 幽 - 1 决 - 1
想 - 13 站 - 6 林 - 4 所 - 3 直 - 2 唯 - 2 薪 - 1 永 - 1 幸 - 1 冬 - 1
心 - 13 眼 - 6 极 - 4 戏 - 3 番 - 2 咻 - 2 蒂 - 1 毫 - 1 幢 - 1 写 - 1
学 - 13 现 - 6 无 - 4 张 - 3 界 - 2 员 - 2 营 - 1 比 - 1 巨 - 1 具 - 1
子 - 13 照 - 6 改 - 4 师 - 3 电 - 2 吱 - 2 菊 - 1 死 - 1 岛 - 1 关 - 1
到 - 13 每 - 6 换 - 4 巷 - 3 由 - 2 北 - 2 茶 - 1 歪 - 1 岂 - 1 先 - 1
面 - 12 意 - 6 惜 - 4 少 - 3 瓜 - 2 动 - 2 苦 - 1 步 - 1 岁 - 1 假 - 1
过 - 12 德 - 6 念 - 4 奇 - 3 珍 - 2 利 - 2 艺 - 1 正 - 1 山 - 1 信 - 1
色 - 12 对 - 6 必 - 4 块 - 3 牛 - 2 冰 - 2 般 - 1 歉 - 1 属 - 1 俗 - 1
自 - 12 女 - 6 并 - 4 场 - 3 烂 - 2 兴 - 2 航 - 1 款 - 1 屐 - 1 例 - 1
店 - 12 台 - 6 层 - 4 圣 - 3 漫 - 2 入 - 2 腿 - 1 欠 - 1 屉 - 1 余 - 1
没 - 11 口 - 6 客 - 4 因 - 3 漠 - 2 光 - 2 脚 - 1 橄 - 1 尽 - 1 低 - 1
当 - 11 却 - 6 字 - 4 哪 - 3 游 - 2 傻 - 2 脑 - 1 横 - 1 尤 - 1 但 - 1
工 - 11 儿 - 6 失 - 4 命 - 3 混 - 2 修 - 2 胸 - 1 榄 - 1 尘 - 1 休 - 1
多 - 11 使 - 6 地 - 4 告 - 3 涯 - 2 伏 - 2 胜 - 1 概 - 1 宿 - 1 仙 - 1
出 - 11 似 - 6 呢 - 4 合 - 3 浸 - 2 从 - 2 肿 - 1 棺 - 1 宽 - 1 仓 - 1
几 - 11 会 - 6 名 - 4 单 - 3 浮 - 2 仍 - 2 肉 - 1 梯 - 1 容 - 1 仁 - 1
们 - 11 份 - 6 加 - 4 华 - 3 浪 - 2 今 - 2 翻 - 1 梨 - 1 害 - 1 亮 - 1
什 - 11 些 - 6 分 - 4 半 - 3 洗 - 2 二 - 2 羡 - 1 梅 - 1 宠 - 1 交 - 1
为 - 11 五 - 6 冷 - 4 千 - 3 洋 - 2 丽 - 2 置 - 1 树 - 1 宜 - 1 亚 - 1
钱 - 10 高 - 5 内 - 4 化 - 3 毛 - 2 两 - 2 罚 - 1 标 - 1 完 - 1 于 - 1
道 - 10 连 - 5 八 - 4 包 - 3 母 - 2 七 - 2 缴 - 1 柜 - 1 安 - 1 争 - 1
还 - 10 足 - 5 书 - 4 别 - 3 此 - 2 黑 - 1 缤 - 1 果 - 1 守 - 1 习 - 1
车 - 10 赚 - 5 食 - 3 切 - 3 歌 - 2 鲜 - 1 缎 - 1 松 - 1 存 - 1 乘 - 1
水 - 10 见 - 5 顾 - 3 六 - 3 欲 - 2 骨 - 1 绣 - 1 杏 - 1 婆 - 1 久 - 1
总 - 10 装 - 5 非 - 3 全 - 3 欢 - 2 验 - 1 绝 - 1 朵 - 1 始 - 1 丢 - 1
开 - 10 第 - 5 青 - 3 克 - 3 案 - 2 骇 - 1 绕 - 1 术 - 1 妇 - 1 世 - 1
国 - 10 竟 - 5 阵 - 3 像 - 3 格 - 2 饭 - 1 练 - 1 替 - 1 套 - 1 专 - 1
和 - 10 空 - 5 问 - 3 代 - 3 根 - 2 飘 - 1 纷 - 1 暗 - 1 奋 - 1 万 - 1
后 - 10 离 - 5 部 - 3 乱 - 3 样 - 2 风 - 1 纳 - 1 普 - 1 奈 - 1 丁 - 1
候 - 10 知 - 5 追 - 3 丝 - 3 板 - 2 颜 - 1 紫 - 1 晚 - 1 夏 - 1
便 - 10 用 - 5 远 - 3 三 - 3 材 - 2 鞭 - 1 紧 - 1 晓 - 1 备 - 1
作 - 10 理 - 5 身 - 3 颗 - 2 木 - 2 靠 - 1 米 - 1 易 - 1 处 - 1
亲 - 10 物 - 5 路 - 3 预 - 2 曾 - 2 陷 - 1 简 - 1 昏 - 1 墨 - 1
之 - 10 清 - 5 趣 - 3 静 - 2 明 - 2 院 - 1 签 - 1 昂 - 1 墙 - 1
能 - 9 深 - 5 赶 - 3 露 - 2 早 - 2 阳 - 1 筛 - 1 旷 - 1 塞 - 1
而 - 9 求 - 5 象 - 3 雾 - 2 新 - 2 阱 - 1 窗 - 1 旁 - 1 堵 - 1
父 - 9 气 - 5 语 - 3 零 - 2 文 - 2 队 - 1 穷 - 1 数 - 1 堕 - 1

Предисловие к книжке Теория и практика жонглирования
zhtw
Я уже писал про книжку. Она замечательная. Решил вот перевести предисловие, написанное знаменитым фокусником Пеном Джтлеттом (да, звучит по-дурацки, но вроде именно так его имя должно склоняться).

-----

Я жонглирую дольше, чем Джейсон Гарфилд жив. Я усилю свое утверждение: я зарабатываю деньги на донглировании дольше, чем Джейсон Гарфилд жив.

Я научился жонглировать, мне было 12. Мне было нечего делать кроме жонглирования — я уж точно не мог трахаться. Мой способ научиться жонглировать — это метод грубой силы. Я тупо продолжал подкидывать вещи в воздух до тех пор, пока они не переставали падать на землю. То есть на самом деле никакого конкретного способа не было, метод заключался в следующем: вкладываешь время и получаешь результат. И я совсем не парился на счет времени. А стоило. Если бы я потратил эти бесконечные часы на тренировку с Джейсоном, это было бы намного эффективнее и по-настоящему намного прикольнее. В 1974-м с Майклом Мошеном Мак-Артуром Гениальным в качестве партнера был очень хорошим жонглером. Вместе мы перекидывали девять булав. Мы думали тогда, что мы первые в мире, кто может перекидывать девять, но оказалось, что чертовы русские нас сделали (девять булав, и Спутник, будь они прокляты!) Майкл и я занимались как ненормальные. Мы занимались шесть полных восьмичасовых рабочих дней в неделю. И это продолжалось месяцами. А когда мы не могли заниматься все время (типа когда нужно было работать или учиться), мы все равно загимались много. Мы занимались много и мы стали крутыми, но опять же, если бы нас тренировал Джейсон, мы бы были круче, намного круче.

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

Я видел, как Джейсон тренирует брата и сестру Гальченко (чертовы русские). Когда они познакомились в Джейсоном, они знали как жонглировать лучше, чем кто-либо. Джейсон не учил их жонглиовать. Но я наблюдал, как он учит их тренироваться. Джейсон научил их учиться, тренироваться и выступать. Они были отличными жонглерами до Джейсона, они стали лучшими в мире после Джейсона. Он продумывает все, что касается я жонглирования. Как-то пару месяцев назад я жонглировал на лужайке с Джейсоном. Он жонглировал пятью булавами за спиной и отрабатывал жонглирование семью, а я пытался контролировать четыре булавы. Он понаблюдал за мной и вежливо спросил, не сможет ли он помочь. Он предложил попробовать стоять в другом положении, дал еще несколько советов. Я думал: «Я начал жонглировать раньше, чем ты родился, ты лысый качек. Иди лучше надень шапку и поотжимайся», но его советы помогли. Благодаря только тем нескольким его комментариям тогда на лужайке я стал жонглировать лучше — и это после 35-и лет.

-----
Оставшиеся пару абзацев переведу чуть позже.

Как настроить публичный репозиторий на Ртути.
zhtw
Я использую Ртуть. Я люблю Ртуть. Если хочется расшарить ртутный репозиторий по http, нет ничего проще, чем использовать встроенный http-сервер.

Просто запускаете hg serv и готово. Чтобы запустить hg serv с несколькими репозиториями, можно ему передать простой файлик конфигурации через ключ --web-conf:

hg serve --web-conf conf

В файлике conf должно быть что-то вроде этого:


[paths]
oop = /var/hg/k806/oop
da = /var/hg/k806/da
os = /var/hg/k806/os
algorithms = /var/hg/k806/algorithms


Можно также указать явно порт, на котором будет слушать встроенный веб-сервер, с помощью ключа -p.

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

Что для этого нужно? Нужно создать каталог, где будет конфигурация вашего репозитория, например /etc/hg-k806. В этом каталоге нужно создать файлик run со следующим содержимым:


#!/bin/sh
exec setuidgid nobody envdir env sh -c 'hg serv -p $port --web-conf conf'


Туда же положить вышеуказанный файлик conf. Там же создать каталог env с файликом port, в который записать номер порта, на котором будет слушать сервер.

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


#!/bin/sh
exec multilog t /var/log/hg-k806


Сами репозитарии должны находится в тех местах, которые указаны в conf.

Все. Теперь, после создания символической ссылки на каталог /etc/hg-k806 из каталога /service (основного каталога, в котором svscan ищет конфигурации сервисов), веб-сервер будет стартовать автоматически. Управлять им можно стандартным способом утилитой svc:

svc -t /etc/hg-k806 — рестартовать,
svc -d /etc/hg-k806 — остановить,
svc -u /etc/hg-k806 — поднять и так далее.

Daemontools берет на себя всю заботу о логах, сохрании pid'а и пр.
Но самое удобное заключается в том, что скрипт run можно запускать и вручную, что удобно для отладки и позволяет автору сервера не думать о проблеме, которая решена уже миллион раз миллионом людей.

С тех пор как я узнал о daemontools, я использую их везде и для всего. Это чрезвычайно удобный набор утилит.
Важно заметить, что распространенный подход, когда каждая программа сама берет на себя заботу о сохранинии pid'а и пр. после daemontools кажется (и является!) убогим. Это, совершенно очевидно, очень плохой подход. Daemontools предоставляет настоящий unix way для демонизации серверов.

Ну и последнее. Если на сервере работает что-нибудь еще, кроме Ртутного веб-интерфейса, имеет смысл запускать его на каком-нибудь своем порту, а на 80 повесить nginx. Это полезно и для настройки виртуального хоста.

Вот фрагмент nginx.conf, который смотрит на наш hg-k806:


server {
 listen 80;
 listen [::]:80;
 server_name hg.k806.ru;
 location / {
  proxy_pass http://localhost:4080/;
 }
}


Как это все выглядит в жизни: hg.k806.ru. Или еще: hg.umc8.ru

Еще про Шелл
zhtw
Давайте я расскажу про другую интересную конструкцию на Шелле. Теперь про такую, которая работает.
Иногда нужно выполнить кое-какие действия над файлом и результат положить в два разных файла. При этом действия легко сделать за один проход.

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


rm -f out2
while read x y; do
  echo $x
  echo $y >>out2
done >out1 <in


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

Как написать хорошо? Элементарно!


while read x y; do
  echo $x
  echo $y >&3
done >out1 3>out2 <in


Разве это не прекрасно? (Я понимаю, что код по-прежнему внешне не симметричен, но нас интересует внутренняя красота: логика работы с out1 и out2 -- одинаковая.)

Очень полезно помнить, что в шелле можно перегружать не только стандартный ввод и вывод (и вывод об ошибках), а вообще любые дискрипторы! Многие об этом забывают.
Tags: ,

Шелл — мой любимый язык программирования
Ĵonglado
zhtw
Все, кто хоть чуть-чуть программировал на шелле, знают о том, что будет, если забысть сделать в нужный момент set -ue, или хотя бы set -e.

Я знаю, что есть приемы написания кода, которые позволяют обойтись без set -e. Но они не универсальные и при постоянном использовании делают код не очень читабельным.

Так вот, мы с вами люди нормальные и мы никогда не забываем в первой строчке писать

#!/bin/sh -ue

(Можно и bash. Не люблю башистов, но на чистом posix-Шелле программировать действительно бывает неудобно.)

Мы также помним (мы же опытные программисты), что set -e отключается внутри условия if-then и умеем с этим жить.

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

Как думаете, вернет ли ошибку вызов функции proc, если файл nonexistent не существует?


proc()
{
  x=$(cat nonexistent)
  ...
}


Да, вернет. Ну, хорошо, а если так?

proc()
{
  local x=$(cat nonexistent)
  ...
}



Объяснение, почему оно так, звучит вполне логично. Но это ж ничего не меняет. Ни один человек без заглядывания в документацию не сможет предсказать такого рода поведение.
Tags: ,

Про шелл
Ĵonglado
zhtw
Почему (head -n $n; cat) <$file не эквивалентно tail -n -$n $file
Давайте я сначала расскажу, почему можно подумать, что это эквивалентные конструкции.

Очень просто:


(head -n $n; cat) <$file


Что тут происходит? Сначала fork, потом закрывается стандартный ввод, потом открывается $file. Дискриптор при этом выделяется 0-й. Дальше дважды fork/exec. Первый раз для head, второй —для cat.

После fork'а для head'а дочерний процесс имеет копию дискриптора родительского процесса. Это значит, что при закрытии стандартного ввода head'ом, файл не закрывается, ведь у родительского осталась копия дискриптора. И текущая позиция в файле тоже осталась прежней. Fork перед exec для cat'а копирует дискриптор еще один раз и cat продолжает читать с той позиции, где head закончил.

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

У head'а нет возможности прочитать со ввода ровно заданное число сторк, потому что для этого бы пришлось читать побайтно. Терминал в обычном режиме буферизирует данные как раз построчно. А вот при чтении из файла с диска, размер буфера определяется через stat.

Поэтому head обычно читает больше, чем мы его просим. (На вывод он копирует, конечно же, столько, сколько нужно.)

Как жаль. А ведь такая красивая конструкция!
Tags: ,

Теория и практика жонглирования Джейсона Гарфилда
Ĵonglado
zhtw
В первую очередь хочется сказать, что это, пожалуй, единственный курс, посвященный спортивному жонглированию. Все движения разбираются очень детально. Курс построен так, что каждый элемент отрабатывается отдельно. Описание очень интересное само по себе. Хороший юмор.

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

Но когда начинаешь выполнять упражнения, оказывается, что все гораздо более прозаично. Я говорю прозаично, чтобы избежать слова скучно. Скучно может показаться человеку, который всерьез думает, что можно научиться чему-то наподобие жонглирования за один день. А жонглирование, как и любой сложный навык, требует очень долгой практики. Ее, конечно, можно назвать скучной, но на самом деле все зависит от вашего отношения. Мне кажется, учиться жонглировать не более скучно, чем учиться играть на пианино. И уж точно не более скучно, чем учиться десятипальцевому методу набора на клавиатуре.

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

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

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

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

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

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

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

К моменту начала занятий по-Гарфилду я уже умел жонглировать четырьмя мячами относительно стабильно (рекорд 340 бросков). Я также умею делать несколько нестандартных патернов тремя мячами. И вот сейчас повторяется история с началом настоящих занятий на фортепьяно и настоящих занятий по изучению слепой печати.

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

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

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

Очень рекомендую.


Поездочка. Про отели и пр.
Ĵonglado
zhtw
Вернулись с Таней из поездки. Решил для интереса выписать некоторые характеристики.

Мы путешествовали 24 дня. Побывали в 5 странах, если считать Гонконг отдельной страной (что формально неверно, но так интересней) и где-то в 17-ти городах (это приблизительно, потому что трудно определить, что значит были).

Маршрут по странам и городам: Гонконг, Китай (Шеньчжень, Гуанчжоу, Ханчжоу, Нинбо, Ханчжоу), Малайзия (Куала-Лумпур, Каджанг, Порт-Диксон, Каджанг, Шах-Алам, Куантан, Чератинг, Куантан, Порт-Диксон, Куала-Лумпур), Сингапур, Филиппины (Анжелес, Манила, Себу, Тагбиларан, Анда, Лобок, Тагбиларан, Себу), Гонконг.

Всего было 9 перелетов: Москва -> Пекин -> Гонконг, Ханчжоу -> Куала-Лумпур -> Сингапур -> Кларк, Манила -> Себу -> Гонконг -> Пекин -> Москва.

21 ночь провели в отелях, одну в самолете, одну в китайском поезде Гуанчжоу -- Ханчжоу и еще две ночи в избушке в местечке Nuts Huts в лесу на берегу реки Лобок на острове Бохол на Филиппинах.

Всего ночевали в 18-ти разных отелях, но в Гонконге два отеля были на самом деле разными этажами в одном здании.

Лучший отель на маршруте -- Geo Hotel в центре Куала-Лумпура в ста метрах от станции Pasar Seni. В номере кроме того, что очень чисто, еще и высоченный (более 3 метров) потолок.

Худший и самый грязный -- Cebu Century Hotel в даунтауне Себу-сити на Филиппинах. Темно, грязно, вода из раковины течет на пол.

Самый чистый отель -- Fragrance Hotel Emerald в Сигапуре (либо куала-лумпурский Geo Hotel, оба очень чистые).

Самый большой и самый дорогой номер -- в Orchid Garden Suits в Маниле, с гостиной и спальней с видом на город (вид был бы хороший, но город -- говно;-).

Самый маленький номер, конечно же, в Гонконге в Maple Leaf Hotel. По цене, кстати, почти такой же как манильский Orchid Garden, что вообще характеризует Гонконг. Номер представляет собой комнату размером в небольшую ванную комнату. При этом в нем есть и туалет, и тумбочка и кондей и даже телевизор на стенке висит. Этакая капсула для ночевки.

Вне конкурса избушки Nuts Huts. Там не дешево, не дорого, не супер чисто, но и не грязно, без удобств, без горячей воды, ручной слив в туалете. Но при всем при том, это одно из лучших мест на маршруте. А причина простая: местечко это основали два Бельгийца. Ну и там все сделано супер-найс, супер по-европейски, хоть и по-простому. Чтобы полноценно описать это место, нужен отдельный пост. Если коротко, то место на берегу реки между двух гор. Речка глубокая, в ней очень приятно купаться. Можно взять каяк напрокат и доплыть до водопадов. На берегу пасутся козы. На склоне горы веранда с рестораном и сауной. Дорожки выложены кокосовыми скорлупками. Ну и все в таком духе. Будете на Бохоле -- обязательно проведите там хотя бы одну ночь. Позвоните и зарезервируйте избушку заранее, потому что место это очень популярное.

Отдельно хочется отметить также отель в Нинбо (Китай). Очень темно, вся подсветка в коридорах густого красно-бордельного цвета. Номер не самый маленький, но туалет прямо в номере за занавеской -- это трындец. Вроде бы ужасное место, но сделано как-то в стиле. Сразу на ум приходит фильм 2046 Вон Карвая. И из-за этого как-то приятно в нем было. (Если вы не смотрели 2046, то вам не стоит в нем останавливаться).

Купались мелакском проливе в Порт-Диксоне (Малайзия), в Южно-Китайском море в Чератинге (Малайзия), в Бохольском море на Филиппинах недалеко от Анды, на юге острова Панглао и в речке Лобок недалеко от города Лобок (тоже на Бохоле).

Самое красивое место -- хм... ну, либо Шоколадные холмы на Бохоле, либо смотровая площадка на крыше отеля Marina Bay Sands в Сингапуре. Это очень по-разному красивые места и я не могу решить какое круче.

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

Самое спокойное место, где можно долго сидеть и ничего не делать (и получать от этого удовольствие) -- Nuts Huts.

Место, куда я бы не раздумывая переехал жить, -- это без сомнений Сингапур. Сингапур -- это фантастический город. В нем круто абсолютно все. То есть совсем. Это город моей мечты. (Там все настолько круто, что хочется его изучать. Я читал историю становления Сингапура Ли Куан Ю и сейчас вот купил еще две его книги про двуязычное образование и про проблемы современного Сингапура. Пока только начал читать, но уже безумно интересно.)