Реактивное программирование в табличном процессоре

Табличный процессор (речь идет о MS Excel или LibreOffice Calc) — это довольно занятный и универсальный инструмент. Мне часто приходилось (и приходится) пользоваться его широкими возможностями: автоматизированные отчеты, проверка гипотез, прототипирование алгоритмов. Например, я использовал его для решения задач проекта Эйлер, быстрой проверки алгоритмов, реализовал парсер одного прикладного протокола (по работе надо было). Мне нравится наглядность, которую можно добиться в табличном процессоре, а еще мне нравится нестандартное применение всего, чего только возможно. На Хабре уже появлялись интересные статьи на тему нестандартного применения
Excel:

«Assembler в 30 строк на Excel»
Чем заняться айтишнику в армии или как я на VBA игры писал
«RPG-игра в рабочей книге Excel»

В этой длинной статье я хочу поделиться своими экспериментами в реактивном программировании с помощью формул табличного процессора. В результате этих экспериментов у меня получился «компьютер» с процессором, памятью, стеком и дисплеем, реализованный внутри LibreOffice Calc при помощи одних только формул (за исключением тактового генератора), который можно программировать на неком подобии ассемблера. Затем, в качестве примера и proof-of-concept, я написал игру «Змейка» и бегущуюползущую строку для этого компьютера.

Предисловие

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

Современные табличные процессоры представляют собой пример реактивного программирования. Ячейки таблицы могут содержать строковые значения или формулу вида «=B1+C1», значение которой будет вычислено исходя из значений соответствующих ячеек. Когда значение одной из зависимых ячеек будет изменено, значение этой ячейки будет автоматически обновлено.

Действительно, любой кто пользовался формулами в Excel знает, что изменив одну ячейку мы меняем связанные с ней ячейки — получается довольно похоже на распространение сигнала в цепи. Все эти факторы и навели меня на следующие мысли: а что если эта «цепь» будет достаточно сложной? являются ли формулы в табличном процессоре Тьюринг полными? можно ли «запрограммировать» формулы, так чтобы получить какие-нибудь нетривиальные результаты? (например сделать тетрис) Т.к. последнее время я использую Ubuntu на работе и дома, то все эксперименты я проводил в LibreOffice Calc 4.2.7.2

Цифровой дисплей 8×8

Начал эксперименты я с реализации дисплея. Дисплей представляет из себя набор квадратных ячеек 8х8. Здесь пригодилось условное форматирование (оно есть и в Excel и в Calc). Выделяем ячейки, заходим в Format/Conditional Formatting/Condition… и настраиваем внешний вид: черный фон, при условии, что в ячейке содержится, например, пробел. Теперь если записать в ячейку пробел, то она становится черной. Таким образом реализуются пиксели нашего дисплея. Но этим дисплеем хочется как-то управлять. Слева от него я выделил специальный столбец в который будут заноситься числа — идея такая, чтобы этим числом мы задавали битовую маску для отображения на экране. Сверху экрана я пронумеровал столбцы. Теперь в каждую ячейку дисплея мы должны написать формулу, которая даст в результате либо пробел, либо пустую строку, в зависимости от того, установлен ли нужный бит в самом левом столбце.

=IF(MOD(TRUNC(<битовая маска>/(2^<номер столбца дисплея>));2);" ";"")

Здесь, по сути, происходит сдвиг вправо (деление на степень двойки и потом отброс дробной части), а затем берется 0-й бит, то есть остаток от деления на 2, и если он установлен, то возвращается пробел, иначе пустая строка.
Теперь при записи в самый левый столбец какого-то числа на дисплее отображаются пиксели. Далее мне хотелось сгенерировать битовых масок, например, для десятичных цифр и, в зависимости от цифры, заполнять столбец масок дисплея нужными числами.
Для генерации была создана еще одна конструкция 8х8, в которую руками заносятся единицы, а формула сворачивает все это в одно число:

=SUMPRODUCT(<строка ячеек с единичками и ноликами>;2^<строка с номерами позиций>)

В итоге получил такую матрицу битовых масок для цифр:

Sign-generator

0	0	24	36	36	36	36	24	0
1	0	8	24	40	8	8	8	0
2	0	24	36	4	8	16	60	0
3	0	24	36	8	4	36	24	0
4	0	12	20	36	60	4	4	0
5	0	60	32	56	4	4	56	0
6	0	28	32	24	36	36	24	0
7	0	60	4	8	16	16	16	0
8	0	24	36	24	36	36	24	0
9	0	24	36	36	28	4	24	0

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

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

INDEX(<матрица>; <цифра> + 1; <номер строки дисплея>+1)

единицы прибавляются потому, что INDEX считает координаты с единицы, а не с нуля.

Циклические ссылки

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

Опции вычислений

d01a237e261749658ef4234ddc80819d[1]

Циклическая ссылка подразумевает под собой формулу в ячейке, зависящую от нее самой же, например, в ячейку A1 мы запишем формулу «=A1+1». Такая ячейка, конечно, не может быть вычислена — когда заканчивается число допустимых итераций, то Calc выдает либо #VALUE, либо ошибку 523. К сожалению, обмануть Сalc не удалось, идея была такая, чтобы сделать одну ячейку постоянно растущей до какого-то предела, например, в A1 я бы записал что-то вроде: =IF(A1<500; A1+1; 0), а в B1, например, такое: =IF(A1=500;B1+1;B1). 500 — это просто магическое число, которое должно было обеспечить задержку, то есть, пока в А1 накапливается сумма, это заняло бы какое-то время, а потом бы поменялся B1. (Ну тут надо было бы еще позаботиться о начальной инициализации ячеек.) Однако, мой план не сработал: в Calc реализованы какие-то хитрые алгоритмы кэширования и проверки (я даже немножко заглядывал в исходники, но подробно не ковырялся), что зациклить вычисление формулы не получается, какие бы хитрые зависимости не были. Кстати в Excel 2003 этот трюк, кажется, частично срабатывал, и, вообще, там похоже другая модель вычисления формул, но я все-таки решил экспериментировать в Calc. После этого я решил сделать счетчик на макросах, а на него уже навешивать все свои зависимости. Один товарищ мне, вообще, подсказал сделать на макросах только синхроимпульс (сигнал clock), а на него уже навешивать счетчики и все что нужно. Идея мне понравилась — макрос получался тривиальным: задержка и смена состояния на противоположное. Сам же счетчик состоит из 4-х ячеек:

Cчетчик от 0 до 9

A B
1 Reset 0
2 Clock [меняется макросом 0 или 1]
3 Old value =IF(B1=1; 0; IF(B2 = 0; B4; B3))
4 New value =IF(B1 = 1; 0; IF(AND(B2 = 1; B4 = B3); IF(B4<9; SUM(B4;1); 0); B4))

Здесь уже предусмотрен сброс для инициализации начальных значений, путем занесения 1 в A1.
Такой счетчик подключается к дисплею из предыдущего раздела, и получается то, что видно на данном видео:

Счетчик + дисплей 8х8

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

Неблокирующий таймер

К моему счастью, оказалось, что в Calc можно сделать так, чтобы основной поток макроса не блокировался. Здесь я немного слукавил и просто «нагуглил» готовое решение, приспособив его под себя. Это решение требовало Bean Shell для LibreOffice. Пакет называется libreoffice-script-provider-bsh. Код состоит из 2х частей: одна на BeanShell, другая на LibreOffice Basic. Честно говоря, полностью в коде я не разобрался… каюсь (не владею Java, BeanShell, да и с объектной моделью LibreOffice не особо знаком), но кое-что все-таки подправил.

BeanShell часть

import com.sun.star.uno.Type;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.lib.uno.helper.PropertySet;
import com.sun.star.lib.uno.helper.WeakBase;
import com.sun.star.task.XJobExecutor;
import com.sun.star.lang.XInitialization;
import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.XPropertyChangeListener;
import com.sun.star.beans.PropertyChangeEvent;
import com.sun.star.lang.EventObject;
import com.sun.star.uno.AnyConverter;
import com.sun.star.xml.crypto.sax.XElementStackKeeper ; // defines a start and a stop routine

// This prevents an error message when executing the script a second time
xClassLoader = java.lang.ClassLoader.getSystemClassLoader();

try {
  xClassLoader.loadClass("ms777Timer_01");
  } catch (ClassNotFoundException e)
  {
  System.out.println( "class not found - compiling" );


public class ms777Timer_01 extends PropertySet  implements XElementStackKeeper
  {

// These are the properties of the PropertySet
  public boolean bFixedRate = true;
  public boolean bIsRunning = false;
  public int lPeriodInMilliSec = 2000;
  public int lDelayInMilliSec = 0;
  public int lCurrentValue = 0;
  public XJobExecutor xJob = null;

// These are some additional properties
  Task xTask =null;
  Timer xTimer = null;

  public ms777Timer_01()  {
    registerProperty("bFixedRate",  (short) 0);
    registerProperty("bIsRunning",  (short) com.sun.star.beans.PropertyAttribute.READONLY);
    registerProperty("lPeriodInMilliSec",  (short) 0);
    registerProperty("lDelayInMilliSec",  (short) 0);
    registerProperty("lCurrentValue",  (short) 0);
    registerProperty("xJob",  (short) com.sun.star.beans.PropertyAttribute.MAYBEVOID);
    xTimer = new Timer();
    }

//XElementStackKeeper
  public void start() { 
    stop();
    if (xJob==null) {return;}
    xTask = new Task();
    lCurrentValue = 1;
    bIsRunning = true;
    if (bFixedRate) {
      xTimer.scheduleAtFixedRate( xTask, (long) lDelayInMilliSec, (long) lPeriodInMilliSec );
      } else {
      xTimer.schedule( xTask, (long) lDelayInMilliSec, (long) lPeriodInMilliSec );
      }
    }

  public void stop() {
    lCurrentValue = 0;
    bIsRunning = false;
    if (xTask!=null) { xTask.cancel();}
    }

  public void retrieve(com.sun.star.xml.sax.XDocumentHandler  h, boolean  b) { }

  class Task extends TimerTask  { 
    public void run()  {   // эта функция вызывается по таймеру и дергает триггер, в который мы передаем либо 0 либо 1
        xJob.trigger(lCurrentValue.toString());
         if (lCurrentValue == 0)
              lCurrentValue = 1;
        else
              lCurrentValue = 0;
      }
    }
  }

System.out.println( "ms777PropertySet generated" );
} // of  if (xClass = null)

Object TA = new ms777Timer_01();
return TA;
LibreOffice Basic часть

Sub clock // эту функцию я повешал на кнопку, чтобы запускать и останавливать "тактовый генератор"
	if isEmpty(oP) then // если запустили первый раз, то создаем эти неведомые объекты в которых я не разобрался
		oP = GenerateTimerPropertySet()
		oJob1 = createUnoListener("JOB1_", "com.sun.star.task.XJobExecutor")
		oP.xJob = oJob1
		oP.lPeriodInMilliSec = 150 // здесь задается задержка
	endif
	
	if state = 0 then // а здесь смена состояния, 0 - означает синхроимпульс остановлен и его надо запустить 
		oP.start()
		state = 1
	else                  // в противном случае означает что синхроимпульс запущен и его надо остановить
		oP.stop() 
		state = 0
	endif
End Sub

function GenerateTimerPropertySet() as Any // функция в которой достается срипт на BeanShell
	oSP    = ThisComponent.getScriptProvider("")
	oScript = oSP.getScript("vnd.sun.star.script:timer.timer.bsh?language=BeanShell&location=document")
	GenerateTimerPropertySet = oScript.invoke(Array(), Array(), Array()
end function

sub JOB1_trigger(s as String) // это триггер который вызывается по таймеру из BeanShell скрипта
	SetCell(1, 2, s)
end sub

sub SetCell (x as Integer, y as Integer, val as Integer) // установить значение в ячейке с координатами X, Y
	ThisComponent.sheets.getByIndex(1).getCellByPosition(x, y).Value = val
end sub

Итак, на лист я добавил компонент кнопку, назвал ее «Cтарт/Стоп» и повешал на нее функцию clock. Теперь при нажатии кнопки, ячейка меняла свое значение на 0 или 1 с заданным интервалом, и поток приложения больше не блокировался. Можно было продолжать эксперименты: вешать какие-то формулы на синхро-сигнал и всячески «извращаться».

Тут я начал думать, чего-бы такого сделать. Вот экран есть, логику, вроде как, любую можно реализовать, есть синхроимпульс. А что, если сделать бегущую строку, или, вообще, «Тетрис»? Это ж у меня получается, практически, цифровая схемотехника! Тут вспомнилась занятная игра по цифровой схемотехнике: kohctpyktop, там одно из заданий было сделать сумматор и память с адресным доступом. Если там это возможно было сделать, значит и тут можно — подумал я. А раз есть экран, значит надо сделать игру. А там где одна игра, там и другая, значит надо сделать возможность делать разные игры… Примерно, как-то так, в мою голову пришла идея сделать процессор, чтобы можно было в ячейки заносить команды, а он бы их считывал, менял свое состояние и выводил на экран то, что мне нужно.

Размышлений было много, проб и ошибок тоже, были мысли сделать эмулятор готового процессора, например Z80 и другие не менее безумные мысли… В конце концов я решил попробовать сделать память, стек, регистры и парочку команд типа mov, jmp, математические же команды типа add, mul, sub и т.д. было решено не делать, ибо формулы Calc уже и так это умеют и даже больше, так что я решил использовать в своем «ассемблере» напрямую формулы табличного процессора.

Память

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

m_address - адрес
m_value_in - значение для записи
m_set - сигнал "записать"
m_value_out - значение при чтении, выходной сигнал
m_clear - сигнал на очистку

Чтобы было удобнее, самое время воспользоваться возможностью именовать ячейки в Calc. Становимся на ячейку, Insert/Names/Define… Это позволит дать понятные имена ячейкам и использовать в формулах уже эти имена. Итак, я дал имена 5ти ячейкам, что описаны выше. Дальше выделил квадратную область 10х10 — это те ячейки которые будут хранить значения. По краям пронумеровал строки и столбцы — чтобы использовать номера столбцов и строк в формулах. Теперь каждая ячейка, хранящая значение, заполняется одинаковой формулой:
=IF( m_clear = 1; 0; IF(AND(m_address = ([ячейка_с_номером_ряда] * 10) + [ячека_с_номером_колонки]; m_set = 1); m_value; [текущая_ячейка])),
логика тут простая: сначала проверяется сигнал очистки, если он выставлен, то обнуляем ячейку, в противном случае смотрим совпадает ли адрес (ячейки адресуются числом 0..99, столбцы и строки пронумерованы от 0 до 9) и выставлен ли сигнал на запись, если да, то берем значение на запись, если нет, то сохраняем свое текущее значение. Протягиваем формулу по всем ячейкам памяти, и теперь мы можем заносить в память любые значения. В ячейку m_value_out заносим следующую формулу: =INDIRECT(ADDRESS(ROW([первая_ячейка_памяти]) + m_address / 10; COLUMN([первая_ячейка_памяти]) + MOD(m_address; 10); 1;0);0), функция INDIRECT возвращает значение по ссылке заданной в строке, а функция ADDRESS как раз возвращает строку со ссылкой, аргументы это ряд и колонка листа, и тип ссылки. Я оформил это таким образом:
3cd86e1153a04e89ac9d048fe1240734[1]
Тут желтым цветом обозначены входные сигналы, в которые можно писать значения, в них формул нет, а красным выделено то, что трогать нельзя, зеленое поле — это выходное значение, оно содержит формулу и на него можно ссылаться в других формулах.

Cтек

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

s_address - адрес откуда начинаются ячейки для хранения, например "Z2"
s_pushvalue - значение, которое надо записать в стек
s_push - сигнал на запись
s_pop - сигнал на извлечение из стека
s_popvalue - выходной сигнал - значение, извлеченное из стека
s_reset - сигнал сброса

Для внутренних структур я завел следующие ячейки:

sp_address - адрес ячейки куда показывает указатель стека 
sp_row - ряд sp_address
sp_column - колонка sp_address
sp - указатель стека, число, например 20 означает что 20 значений уже сохранено в стек и следующее будет 21-е
oldsp - старый указатель стека, нужен для корректной работы sp

Ну и осталась длинная строка ячеек, в которых будут храниться значения. Начнем с формулы для извлечения значения s_popvalue =IF(s_pop=1; INDIRECT(sp_address; 0); s_popvalue), тут все просто, если сигнал для извлечения подан, то просто берем значение ячейки по адресу, куда показывает указатель стека, иначе сохраняем старое значение. Формулы для внутренних структур:

ячейка формула
sp_address =ADDRESS(sp_row; sp_column; 1;0)
sp_row =ROW(INDIRECT(s_address))
sp_column =COLUMN(INDIRECT(s_address)) + sp
oldsp =IF(AND(s_push = 0; s_pop = 0); sp; oldsp)

Здесь легко заметить, что для формирования адреса, куда показывает стек, мы берем адрес начала стека и прибавляем к нему указатель стека. Старое значение указателя стека обновляется в случае когда оба сигнала: и на запись и на извлечение — нулевые. Пока все просто. Формула для sp же довольно сложна, поэтому я приведу ее с отступами, для лучшего понимания:

Указатель стека sp

=IF(s_reset = 1;                            // если сигнал сброса, то 
    0;                                      // сбросить указатель в 0
    IF(AND(sp = oldsp; c_clock = 1);        // иначе проверяем равен ли стекпойнтер старому значению и взведен ли синхросигнал (то есть надо ли обновить стекпойнтер) 
        SUM(sp; IF(s_push = 1;              // если обновление стекпойнтера требуется, значит к старому значению прибавляем некое смещение (-1, 0 или 1)
                    1;                      // прибавляем к стекпойнтеру 1, в случае если сигнал push
                    IF(s_pop=1;             // в противном случае, если сигнал pop, то прибавляем либо 0 либо -1
                        IF(sp > 0; -1; 0);  // -1 прибавляем в случае, когда sp > 0, иначе прибавляем 0, то есть оставляем старое значение
                        0)));               // старое значение оставляем в случае когда ни push ни pop не взведены
        sp))                                // если стекпойнтер не равен старому значению, или синхросигнал невзведен то сохраняем старое значение

5 вложенных IF выглядят монстрообразно, в дальнейшем я такие длинные формулы разделял на несколько ячеек так, чтобы в каждой ячейке было не больше 2-х IF’ов.

Осталось привести формулу для ячеек, хранящих значение:

 =IF (s_reset = 1; 0; IF (AND(s_push = 1; ROW([текущая_ячейка]) = sp_row; SUM(COLUMN([текущая_ячейка]); 1) = sp_column; oldsp <> sp); s_pushvalue; [текущая_ячейка]))

здесь в принципе можно «распарсить» без отступов, суть такова, что проверяется некоторое условие и в случае, когда это условие выполняется — в ячейку заносится s_pushvalue. Условие следующее: должен быть взведен сигнал s_push; ряд ячейки должен совпадать с рядом, куда указывает sp; колонка, куда показывает sp, должна быть на 1 больше, чем колонка нашей ячейки; ну и sp не должен равняться своему старому значению oldsp.

Картинка для наглядности, что у меня получилось:
e03588c4cbb047d0a76a128eac6dc6d8[1]

Процессор

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

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

Структуры CPU

Входы:
  c_reset - сигнал сброса (обнуляет состояние процессора)
  c_main - адрес начала программы, точка входа
  c_clock - синхроимпульс, подается извне
  pop_value - значение из стека, подключается к стеку =s_popvalue

Внутренние структуры:
  command - команда на выполнение
  opA - первый операнд команды
  opB - второй операнд команды
  cur_col - текущий ряд (куда показывает ip)
  cur_row - текущая колонка
  ip - instruction pointer, указатель на команду
  oldip - старый ip, нужен для корректной работы ip
  ax - регистр общего назначения (РОН)
  bx - РОН
  cx - РОН
  rax - копия ax, нужна для того, чтобы корректно модифицировать значение ax
  rbx - копия bx
  rcx - копия cx

Выходы:
  mem_addr - адрес памяти, подключено к памяти
  mem_value - значение для записи в память или считанное из памяти
  mem_set - сигнал для записи в память, подключен к памяти

  pop_value - значение из стека, или для записи в стек, подключено к стеку
  push_c - сигнал записи в стек
  pop_c - сигнал чтения из стека

Вкратце, как все работает: входы подключены к тактовому генератору и сбросу (который я повесил на кнопку для удобства, чистая формальность), точка входа настраивается вручную. Выходы подключены к памяти и стеку, на них, в зависимости от команд, будут появляться нужные сигналы. Команда и операнды заполняются, в зависимости от того, куда показывает указатель инструкций ip. Регистры меняют свое значение, в зависимости от команд, и операндов. ip тоже может менять свое значение, в зависимости от команды, но по-умолчанию он просто увеличивается на 1 на каждом шаге, а начинается все с точки входа, которую указывает человек. Т.о. программа может располагаться в произвольном месте листа, главное — адрес первой ячейки указать в c_main.

Список команд поддерживаемый процессором:

mov  - поместить значение в регистр, первый операнд имя регистра, второй - значение, например mov ax 666
movm - поместить значение по адресу в памяти, первый операнд - адрес в памяти, второй операнд значение 
jmp  - переход, один операнд - новое значение ip, второй операнд отсутствует (но в ячейке все-равно должно что-то быть! Магия Calc, которую я не разгадал...)
push - достать значение из стека и положить в регистр общего назначения, единственный операнд - название регистра (ax, bx или cx), магия со вторым оператором такая же
pop  - положить значение в стек, операнд - значение
mmov - достать значение из памяти и положить в регистр, первый операнд - адрес памяти, второй операнд - название регистра

В качестве операндов и команд, в программе можно указывать формулу, главное — чтобы в ячейке в результате получилось значение, именно значения будут попадать на обработку в процессор.
Начнем с простых внутренних структур: cur_col=COLUMN(INDIRECT(ip)) и cur_row=ROW(INDIRECT(ip)) это просто текущий ряд и текущая колонка. command=IFERROR(INDIRECT(ADDRESS(ROW(INDIRECT(ip));COLUMN(INDIRECT(ip)); 1;0); 0); null) здесь уже видно различие теории и практики. Во-первых, пришлось вставить проверку на ошибки. Во-вторых, в формуле пришлось отказаться от предыдущих значений cur_col и cur_row — это приводило к каким-то хитрым циклическим зависимостям и не давало корректно работать ip, впрочем речь об ip ниже. В-третьих, здесь я применил специальное значение null (в случае ошибки), для него выделена отдельная ячейка с «-1».

Значения операндов формируются из текущей строки и колонки со смещением:

opA=IFERROR(INDIRECT(ADDRESS(cur_row; cur_col + 1; 1;0); 0); null)
opB=IFERROR(INDIRECT(ADDRESS(cur_row; cur_col + 2; 1;0); 0); null)

Формула для instruction pointer:

ip=IF(c_reset = 1;                            // проверка на сброс
    c_main;                                 // если был сброс, то возвращаемся на мейн
    IF(AND(c_clock = 1;ip=oldip);           // в противном случае проверяем надо ли обновлять значение (взведен клок и старое значение совпадает с текущим)
        IF(command="jmp";                   // если значение менять надо, то проверяем является ли ткущая команда переходом
            opA;                            // если текущая команда jmp, тогда берем новое значение из операнда
            ADDRESS(ROW(INDIRECT(ip))+1;    // если текущая команда не jmp, тогда просто переходим на следующий ряд
                    COLUMN(INDIRECT(ip))));
        ip))                                // если значение обновлять не надо, то оставляем старое

Фактически, эта длинная формула у меня разнесена по нескольким ячейкам, но можно и все в одну записать.
opdip=IF(c_clock = 0; ip; oldip)

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

Регистры общего назначения

Адреса ячеек чисто условные, для примера.

A B C D E
1 =IF(c_reset = 1; 0; B1) =IF (c_clock = 1; C1; ax) = IF(c_clock=1; IF (opA = «ax»; D1; IF(opB = «ax»; E1; ax));ax) =IF(AND(opA = «ax»;c_clock=1);IF (command = «pop»; pop_value; IF (command = «mov»; opB; ax)); ax) = IF(AND(opB=«ax»;command = «mmov»); mem_value; ax)

Здесь A1 и является, собственно, регистром ax, а остальные это вспомогательные ячейки.

Копия регистра rax=IF(c_reset= 1; 0; IF(AND(rax<>ax; c_clock=0); ax; rax))
Думаю тут совсем не сложно догадаться что происходит. Остальные регистры bx и cx устроены аналогичным образом.

Осталось дело за малым — выходные сигналы процессора:

push_value =IFERROR(IF(command=«push»; opA; push_value);null)
push_c =IF(command=«push»; c_clock; 0)
pop_c =IF(AND(command=«pop»; c_clock = 1); 1; 0)
mem_addr =IF(c_reset = 1; 0; IF(OR(command = «movm»; command = «mmov»); opA; mem_addr))
mem_value =IF(c_reset = 1; 0; IF(command = «movm»; opB; IF(command=«mmov»; m_value_out; mem_value)))
mem_set =IF(c_reset = 1; 0; IF(command = «movm»; 1; 0))

Это сигналы для работы с памятью и стеком. На первый взгляд, сигналы push_c и pop_c, вроде бы, одинаковы по-сути, но формулы в них немножко разные. Могу лишь ответить, то, что они получены методом многочисленных проб и ошибок. В процессе отладки всей этой конструкции было много багов, и они еще остались, к сожалению процессор не всегда работает «как часы». По каким-то причинам, я остановился именно на таком варианте, значит «по-другому» что-то не работало. Сейчас уже не смогу точно вспомнить — что именно.

Картинка моего процессора:
bd856de810d24284bf8f21fac1260bcd[1]

Здесь видно еще debug поля — в них выводятся не значения, а формулы в виде текста.

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

Итак, компьютер готов, можно приступать к написанию программы. В процессе программирования обнаружилось несколько проблем, некоторые из которых были решены, некоторые все же остались:

  1. Иногда «компьютер» глючит и ведет себя непредсказуемо
  2. Надо чтобы на листе было видно почти все, включая программу, иначе ячейки, которые далеко за пределом видимости не обновляют свое содержимое
  3. «Компьютер» получился медленный, уменьшение задержки между тиками приводит к тому, что дисплей и некоторые формулы не успевают обновляться. Опытным путем я подобрал, более менее, оптимальную задержку для своего ноутбука: 150-200 мс

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

Интерфейс ввода, т.е. кнопки управления, пришлось сделать на макросах: 4 кнопки со стрелками и 4 ячейки в которые помещается 1, если кнопка нажата, которые я назвал key_up, key_down, key_left и key_right. К ним был прикручен триггер key_trigger=IF(key_up; «U»; IF(key_down; «D»; IF(key_left; «L»; IF(key_right; «R»; key_trigger)))), в котором сохраняется последняя нажатая клавиша.

Также я сделал кнопку «Debug», для отладки программы, с помощью нее можно руками управлять тактовым генератором и смотреть как меняются состояния ячеек (она заносит попеременно 1 или 0 в ячейку clock). Это все за что отвечают макросы: тактовый генератор и органы управления. Больше макросов не будет.

Начал разработку «Змейки» с псевдокода:

Псевдокод ‘Змейки’

Для «Змейки» нужны следующие сущности: координаты головы; координаты хвоста; массив, где хранятся координаты всех точек змейки; координаты мячика.

HEAD // ядрес ячейки памяти с координатами головы
TAIL   // ядрес ячейки памяти с координатами хвоста
BXBY = rand           // координаты мячика
HXHY = *HEAD      // координаты головы
TXTY = *TAIL        //  координаты хвоста

loop:
	read DIRECTION  // считываем направление (клавишу)
	HEAD++ // увеличиваем указатель головы на единицу
	HXHY += DIRECTION // векторно прибавляем направление к координатам головы
	[HEAD] = HXHY // сохраняем в память новые координаты головы
	BXBY <> HXHY ? JMP cltail // если координаты головы не совпали с координатами мячика, то прыгаем на "стирание хвоста"
	BXBY = rand // генерируем новые координаты мячика
	[BY] = OR([BY]; 9-2^BX) // рисуем мячик на экране (первые 20 ячеек памяти отображаются на экране 10х20)
	JMP svtail //перепрыгиваем стирание хвоста
cltail:
	[TY] = AND([TY]; XOR(FFFF; (9-2^TX))) // стираем хвост с экрана
	TAIL++ // увеличиваем указатель хвоста 
	TXTY = [TAIL] // берем новые координаты хвоста из памяти
svtail:
	[HY] = OR([HY]; 9-2^HX) // рисуем голову на экране

	JMP loop // переходим на начало цикла

Вот такой несложный алгоритм получился.
Хранить данные я решил в аггрегированном виде в регистрах, например регистр ax хранит BXBYHHTT, то есть фактически 4 двузначных переменных: координаты мячика (BX и BY), номер ячейки с координатами головы (HH), номер ячейки с координатами хвоста (TT). Это усложняет доступ к переменным, но позволяет уменьшить число строк программы.

Далее нужно было этот алгоритм детализировать. Начнем с инициализации:

Инициализация

Command Operand 1 Operand 2 Comment
mov ax =RANDBETWEEN(0;9) * 1000000 + RANDBETWEEN(0;19)* 10000 + 2120 BXBYHHTT
movm 21 509 Head: x — 5, y — 9
movm 20 409 Tail: x — 4; y — 9
mov cx R direction init
mov bx 5090409 HXHYTXTY
movm =MOD(ROUNDDOWN(rax/10000);100) =2^(9-ROUNDDOWN(rax/1000000)) draw ball

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

Псевдокод приближенный к рабочему

loop:
	cx = IF(OR(AND(rcx="U";key_trigger="D");AND(rcx="D";key_trigger="U");AND(rcx="L";key_trigger="R");AND(rcx="R";key_trigger="L"));rcx;key_trigger)
	ax = IF(ROUND(MOD(rax;10000)/100) < 89; ROUND(MOD(rax;10000)/100)+1; 20) * 100 + MOD(rax;100) + ROUND(rax/10000) * 10000
	bx = IF(AND(rcx="U";MOD(ROUND(rbx/10000);100)>0);rbx-10000;IF(AND(rcx="D";MOD(ROUND(rbx/10000);100)<19);rbx+10000;IF(AND(rcx="R";ROUND(rbx/1000000)<9);rbx+1000000;IF(AND(rcx="L";ROUND(rbx/1000000)>0);rbx-1000000;"FAIL"))))
	push cx
	[ROUND(MOD(rax; 10000)/100)] = ROUND(rbx/10000)
	jmp IF(ROUND(rax/10000) <> ROUND(rbx/10000); ctail; next)
	ax = MOD(rax;10000) + MOD(MOD(ROUND(rax/10000);100)*11 + 3; 20) * 10000 + MOD(ROUND(rax/1000000)*3+2;10)*1000000 // ball generator
	cx = [MOD(ROUND(rax/10000);100)] // get [BY]
	[MOD(ROUND(rax/10000);100)] = BITOR(rcx; 2^(9-ROUND(rax/1000000))) // draw ball on scr
	jmp svtail
ctail:
	cx = [MOD(rbx;100)] // cx = [TY]
	[MOD(rbx;100)] = BITAND(rcx; BITXOR(HEX2DEC("FFFF"); 2^(9-ROUND(MOD(rbx;10000)/100)))) // clear tail on scr
	ax = IF(MOD(rax;100) < 89; rax + 1; ROUND(rax/100)*100 + 20)
	cx = [MOD(rax;100)] // cx = [TT]
	bx = ROUND(rbx/10000)*10000 + rcx
svtail:
	cx = [MOD(ROUND(rbx/10000);100)] // cx = [HY]
	[MOD(ROUND(rbx/10000);100)] = BITOR(rcx; 2^(9-ROUND(rbx/1000000))) // draw head on scr
	pop cx
	jmp loop

Здесь я заменил переменные псевдокода на регистры, в ax решил хранить 4 двузначных числа: BXBYHHTT, в bx HXHYTXTY, то есть координаты головы и хвоста, а в cx — направление, ну и использовать его для промежуточных нужд. Например, когда надо переложить из памяти в память, напрямую этого сделать нельзя, приходится делать через регистр.

Дальнейшим шагом было только заменить присваивания на команды mov, movm и mmov соответственно и перенести код в ячейки на листе.

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

Для упрощения, проверок на то, что мячик появился посреди змеи не делается. Также не делается проверок на проход змеи сквозь себя.

Работает программа очень «слоупочно». Я записал видео в реальном времени и ускоренное в 16 раз. В конце видео я прохожу сквозь себя и врезаюсь в стену (в регистре bx появляестя «FAIL» и змейка больше никуда не ползет).

Ускоренное в 16 раз видео:

Реальное время

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

Видео ускорено в 16 раз:

Проект доступен на гитхабе, для работы требуется LIbreOffice Calc с установленным BeanShell.

Оригинал: [здесь]

Глобальная значимость английского, немецкого, русского, китайского и других языков в Интернете (Data Mining)

98fdf861bfb24fbbbfb1d0e45ffe9b67[1]
Центральные языки на этой карте могут и не иметь самого большого количества носителей, однако они служат «общими» языками для общения элит.

В молодом направлении Big Data есть свои восходящие звезды и многообещающие лидеры, один из самых ярких это Цезарь Хидальго — профессор MIT Media Lab, разработчик онлайн-платформы визуализации данных о торговых связях между разными странами мира Observatory of Economic Complexity, и один из “50 человек, которые изменят мир” по версии журнала Wired.

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

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

Первый вопрос, конечно, в том, как измерить глобальное влияние языка. До этого исследования опирались на численность и благосостояние носителей языка. Однако исторически сложилось так, что распространение языка требовало серьезной политической поддержки, поэтому такие показатели не сильно влияют на глобальность языка, так как его носители и их богатство могут быть сосредоточены в относительно малом масштабе. Другой метод измерения глобального влияния языка состоит в определении того, кто является его носителем, а также на связь между носителями. Лингвист Дэвид Кристалл утверждает: «Популярность языка не имеет ничего общего с количеством его носителей. Намного важнее то, кем они являются». В прошлом латынь была общеевропейским языком не потому, что она была родным языком для большинства европейцев, а потому, что являлась языком Римской империи, а позже Католической церкви, ученых и педагогов. Использование латыни элитами и связь между ними, помогли ей продержаться в качестве универсального языка более 1000 лет.

При этом в современном мире языковая карта выглядит примерно вот так (ещё раз):
fd8449587a8d42ef9f8d2e40db3e37bf[1]

Фулсайз
Для начала посмотрим на коллекцию более 2,2 миллионов книжных переводов, составленную по проекту ЮНЕСКО. Этот набор данных позволяет отобразить сеть книжных переводов от авторов и профессиональных переводчиков. Он строился на основе рыночного спроса на книги на разных языках. Каждый перевод с одного языка на другой формирует связь. Затем мы наносим на карту мультиязычную сеть, используемую редакторами Википедии. Здесь связь между языками образуется, когда пользователь редактирует статью в одной языковой версии Википедии и со значительной долей вероятности отредактирует ее в другой языковой версии. И наконец, мы наносим на карту сеть совместного использования языков в Твиттере. Здесь связь образуется, если пользователь пишет сообщение на одном языке и с большой долей вероятности напишет его на другом.

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

422cb1319d5c4220bccd6b8593a5c16a[1]

Фулсайз
На этих иллюстрациях — сходство трех независимых наборов данных, которые мы используем для отображения GLN. Верхний ряд показывает зависимость между числом выражений для каждого языка во всех трех наборах данных: (А) редактирование статей в Википедии на языке и перевод книг с языка; (В) сообщения в Твиттере на языке и перевод книг с языка; (С) пользователи в Твиттере и редакторы в Википедии. Нижний ряд показывает соотношение между количеством одинаковых фраз для языковых пар в различных наборах данных: (D) общее количество книжных переводов и редакторов Википедии; (Е) общее количество пользователей Твиттера и книжных переводов; (F) пользователи Твиттера и редакторы Википедии. В D и Е мы систематизировали среднее количество переводов с языка и на язык.

Влияние глобальных языков

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

68dae5cd4f754fd69c4e8f600e8b6370[1]

Фулсайз
На иллюстрации положение языка в GLN и глобальное влияние его носителей. Верхний ряд показывает количество носителей языка (годы рождения 1800-1950), о которых написаны статьи по крайней мере в 26 языковых версиях Википедии, как функции центральности собственного вектора языка в (А) GLN Твиттера, (В) GLN Википедии и (С) GLN книжных переводов. Нижний ряд показывает количество носителей языка (годы рождения 1800-1950), о которых есть упоминания в «Human Accomplishmentas», как функции центральности собственного вектора языка в (D) GLN Твиттера, (E) GLN Википедии и (F) GLN книжных переводов. Размер зависит от количества носителей каждого языка, а интенсивность цвета — от ВВП на душу населения.

Затем Цезарь собрал данные Твиттеру из более чем одного миллиарда твитов, опубликованных между 6 декабря 2011 и 13 февраля 2012. Язык каждого твита был обнаружен с помощью Chromium Compact Language Detector после очистки от хештегов, ссылок и смайликов. Использовались только те сообщения, где шанс ложного срабатывания был менее 10%. Конечный набор данных состоит почти из 550 миллионов твитов на 73 языках, созданных более 17 миллионами уникальных пользователей. Два языка считаются связанными, если пользователь написал твит на одном языке и с большой долей вероятности напишет его на другом.

Набор данных из Википедии был составлен при редактировании истории всех языковых разделов Википедии, написанных в конце 2011 года. После удаления информации от ботов и применения фильтров, получилось 382 миллиона правок на 238 языках от 2,5 миллионов уникальных редакторов. Здесь два языка оказались связанными, если пользователь отредактировал статью на одном языке и с большой долей вероятности сделал это же на другом.

Набор данных индекса переводов (ИП) состоит из 2,2 миллионов переведенных книг, изданных между 1979 и 2011 в 150 странах более чем на тысяче языков. Набор данных содержит список переводов, а не список переведенных книг. Каждый перевод в нем учитывается отдельно, например, 22 независимых перевода «Анны Карениной» Толстого с русского на английский. В отображении сети мы учитываем каждый перевод отдельно, и в этом случае учитывались 22 перевода, а не один. Также отметим, что источник перевода может отличаться от языка оригинала книги. Например, в ИП содержатся данные о 15 переводах «Тома Сойера», причем 13 из них были сделаны непосредственно с английского, а 2 — с испанского и галисийского. Эта характеристика набора данных позволяет определить промежуточные языки для перевода.

Во всех трех случаях мы изобразили похожие языки в соответствии со стандартом ISO 639-3(10). Например, индонезийский и малазийский языки закодированы как малайский, а все диалекты арабского языка — как арабский.

Результаты

5df521f648bb499290214de3d662c8dd[1]

Фулсайз
На иллюстрации сосредоточенность и количество известных людей в GLN для каждого языка согласно Википедии. (A) Количество человек(рожденных в 1800-1950) для каждого языка, о которых есть статьи в по крайней мере 26 языковых вариантах Википедии в зависимости от ВВП на душу населения, численности населения и центральности собственного вектора для каждой GLN. Рейтинг культурного производства: (B) страны и (С) языки с наибольшим количеством людей, о которых есть статьи минимум в 26 языковых версиях Википедии. Собственный рейтинг значения языков в GLN: лучшие семь языков в (D) Твиттере, (E) Википедии и (F) сети книжных переводов.

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

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

Можно утверждать, что периферийное расположение языков хинди, китайского и арабского в GLN происходит из-за недостаточного представления в мире этих и некоторых других языков, которые к ним подключаются. Эти языки могут быть центральными в различных СМИ, однако их слабая роль в трех глобальных сетях — Твиттер, Википедия и сеть книжных переводов, ослабляет их претензии на глобальное влияние. Кроме того, китайский, арабский и хинди не могли бы быть центральными языками, даже если бы в базах данных была бы лучше указана их связь с различными региональными языками, так как центральный язык должен соединять даже очень отдаленные языки, а не только местные.

Лекция

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

Цезарь будет выступать у нас в Digital October 22 апреля с открытой лекцией (телемост) «Почему объем информации все время растет?». Язык – английский, плюс каждый участник получает радиоустройство синхронного перевода на русский, поэтому проблем с пониманием не будет. Регистрируйтесь на мероприятие и заходите в гости, будет интересно.

Оригинал: [здесь]

Вести с полей или зачем нужна Бизнес-информатика

Оригинал: Татьяна Короткая — Челябинцы готовы дать в глаз своим коллегам, Комсомольская правда, 25 февраля 2013

Как и почему горожане конфликтуют на работе

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

— Это начальник отдела продаж с логистами лается, — равнодушно комментирует подруга. — Каждый день одно и то же. То товар вовремя не привезли, то с позициями напутали…
Читать далее «Вести с полей или зачем нужна Бизнес-информатика»

MOOCs — реальный старт в развивающихся странах

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

Такие образовательные онлайн-платформы, как Coursera, EDX и Udacity, стремительно вышедшие на сцену в прошлом году, по утверждению своих сторонников имеют огромный потенциал для демократизации высшего образования в странах, где доступ к нему наименьший (см. «Важнейшие технологии образования за последние 200 лет»). Сегодня такие амбиции становятся реальностью постольку, поскольку все больше людей, не смотря на серьезные проблемы, начинают экспериментировать с их установкой.
Великое множество студентов из Индии и Бразилии регистрируются на этих массовых открытых онлайн-курсах или, так называемых MOOCs — Massive open online course, продолжая свое бесплатное обучение с такими ведущими университетами, как Стэнфорд, Массачусетский технологический институт и Гарвард.
next.step_.moocsx299[1]
Однако, в беднейших регионах мира, где иногда затруднен надежный высокоскоростной доступ в Интернет для передачи потокового видео курса лекций, доставка образовательного контента в массы явно не простая задача, поэтому эксперименты организационного порядка здесь только начинаются (см. «Онлайн курсы оказывают давление на университеты беднейших стран»).
Одна из главных задач курсов MOOCs, которые до сих пор, в основном, поступают из университетов США — это адаптация содержания к разнообразию аудиторий всего мира, к любым комбинациям языков, образовательным, мотивационным и культурным традициям. Критики опасаются большого роста образовательных кейсов всего от нескольких элитных учреждений западных стран, но это беспокойство беспочвенно в силу не соответствия различных традиций обучения у разных народов.

Такая ситуация, чисто с точки зрения курсов, начинает меняться и эти стартапы расширяются предложениями сотрудничества с международными университетами. Так, например, в феврале (2013 года) некоммерческая платформа EDX, созданная в прошлом году силами Гарварда и MIT, в качестве партнера дополнилась Федеральной политехнической школой из Лозанны, Швейцария. И хотя первые курсы проводятся на английском языке, в Школе, согласуясь с задачами развития Гражданского общества, по предложению представителей EDX теперь задумали распространить их на франкоязычные регионы Восточной и Центральной Африки.

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

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

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

В Индии, например, Microsoft Research, у которого офисы в Бангалоре, сотрудничает с университетами, используя «уполномоченные классы», где проводятся онлайн лекции, форумы и викторины для студентов инженерных специальностей различных школ, совмещенные с тем же курсом информатики. Еще одна интересная идея из Индии — это проект Microsoft Research по сканированию электронных учебников на предмет извлечения наиболее важных понятий, которые в дальнейшем могут быть интегрированы в обучающее онлайн видео. Таким образом, индийские профессора могут обсуждать воздействие электромагнитных полей вместе с показом ряда диаграмм из учебников по физике. Еще один проект, называется VidWiki и позволяет любому комментировать видео текстом на родном языке.

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

Самые грандиозные планы не могут приходить только от технологий.

Прямо сейчас, в Руанде, некоммерческая организация Generation Rwanda приступает к работе над амбициозным экспериментальным проектом, вероятно, одним из первых в своем роде — университет, полностью построенный на MOOCs.

Хотя в конце этого года (2013) будет работать только пилотный вариант, конечной целью является создание университета в Руанде для 400 человек, с обеспечением занятий всех студентов только посредством MOOCs и путем обсуждения проблемных областей для стипендиатов (коммерческих студентов). «Для начала, первые студенты попробуют курс Гарвардского университета по юриспруденции и курс Университета Эдинбурга Критическое мышление и глобальные вызовы«, — говорит исполнительный директор создаваемого Университета Jamie Hodari (Джейми Ходари). Как только будет достигнуто соглашение о партнерстве с Southern New Hampshire University — Новый южный Хэмпшерский университет, по тестированию и сертификации, Университет начнет работать.

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

Хотя только 1% руандийцев смогут с помощью интернет-технологий достичь степени бакалавра, Hodari не ждет. Ссылаясь на то внимание, которое приобрело MOOCs за последний год он утверждает — «Нам тяжело понять, прочитав все эти посылы, что уже сейчас можно учиться где нибудь в Судане и получать первоклассное высшее образование бесплатно. Сегодня нам кажется, что это очень далеко от реальности и на самом деле доступно не всем, а только немногим избранным».

Источник: MIT Technology Rewiev

Джессика Лебер, 15 марта 2013

Все праздники ИКТ-шников

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

Международный день без интернета

Данный праздник придумали в 2002 году организаторы британского некоммерческого онлайнового проекта DoBe.org, которые объявили последнее воскресенье января Международным днем без интернета. В этом году он отмечался 25 января. По их замыслу этот день люди должны провести в оффлайн, то есть без выхода во всемирную сеть и без компьютера. Пользователи сети, вместо онлайн-общения, должны выйти на прогулку, выехать за город, навестить родственников и друзей. Для того чтобы выбрать способ проведения времени без интернета, DoBe.org предлагает на листе бумаги написать шесть вариантов проведения досуга, а затем бросить игральную кость, чтобы определить, какой из них осуществить в первую очередь. Таких дней без интернета уже 19.

День безопасного интернета

Этот праздник был учрежден по инициативе Европейской комиссии в 2004 году. Он отмечается в первый вторник февраля. В 2009 году празднование пришлось на 2-е число месяца. Целью дня безопасности в интернете является информирование пользователей сети о рисках и опасностях, связанных со всемирной паутиной. Праздник отмечается по всему миру. Праздник отмечают 11 раз.

День компьютерщика

Праздник отмечается 14 февраля. Это день всех влюбленных в компьютеры людей. А если серьезно, то 14 февраля 1946 года был запущен первый электронный цифровой компьютер ENIAC, который реально работал и даже совершал вычисления (обсчет баллистических таблиц армии США). В честь этого события и задумали праздник, отмечать который теперь могут миллионы пользователей компьютеров во всем мире.

День оверклокера

Это праздник для компьютерных энтузиастов, которые «разгоняют» процессоры своих ЭВМ до неведомых скоростей. По легенде впервые упоминание об этом празднике было зафиксировано в форуме российского оверклокерского портала в 2004 году. Некто под ником «зЁма с чернозЁма» предложил: «…давайте назначим себе дату — День оверклокеров (а то день танкиста есть, у лесной промышленности тоже… абыдно жить без праздника)». C тех пор «День оверклокера» отмечается в обычный год 28 февраля, а в високосный — «разогнанный год» — 29 февраля. Причем этот праздник, зародившийся в России, отмечается теперь по всему миру.

День ИТ-специалистов

Является неофициальным праздником. Отмечается 28 февраля в день изобретения сетевого кабеля. В основном — это еще один повод выпить с коллегами.

День свободы слова в интернете

Этот праздник создан по инициативе международной организации «Репортеры без границ» и проходит под патронатом ЮНЕСКО. День свободы слова в интернете — мероприятие сравнительно молодое — впервые он отмечался 12 марта 2008 года. Его целью является поддержка интернет-диссидентов, которые отбывают тюремное заключение по всему миру. В прошлом году в застенках находилось 63 человека, чья свобода слова не понравилась властям.

Организаторы мероприятия призывают в этот день выразить в виртуальном пространстве протест против цензуры, которой злоупотребляют правительства некоторых стран. К таким относятся Бирма, Китай, Северная Корея, Куба, Египет, Эритрея, Тунис, Туркменистан, Вьетнам и другие.

День выключения (Shutdown Day)

Впервые этот праздник отмечался 24 марта 2007 года. Тогда в Сети появился призыв отключить в один день как можно больше компьютеров по всему миру. Цель акции заключается в том, чтобы узнать, сколько же людей может продержаться в течение суток без компьютера, и что может произойти в результате такого флешмоба. Авторами идеи стали программисты, проживающие в Монреале (Канада), Denis Bystrov (родился в Белоруссии) и Ashutosh Rajekar (родился в Индии).

День вэб-мастера

4 апреля отмечается День веб-мастера (или Вебмастера). Дата этого праздника выбрана неслучайно: если присмотреться, можно заметить, что цифры 4.04 очень напоминают по своему написанию ошибку 404 («Страница не найдена»), имеющую прямое отношение к работе веб-мастеров. К тому же эта дата совпадает с Международным днем интернета. Веб-мастер — профессия довольно новая, она появилась совсем недавно, в век развития интернета. Веб-мастер, или «управляющий» сайтом, — это человек, занимающийся разработкой веб-сайта или корпоративного приложения в интернете.

Впервые термин «веб-мастер» ввел в обращение «праотец» Интернета Тим Бернерс-Ли в документе «Руководство по стилю гипертекста в онлайне» в 1992 году. В начале 1990-х, когда «общедоступный» Интернет еще только начинал развиваться, функционал первых веб-мастеров был очень разнообразным: он включал в себя обязанности веб-дизайнера, автора и модератора сайта, программиста, системного администратора, контент-менеджера (ответственного за смысловое наполнение сайта), сотрудника технической поддержки сайта

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

День рождения Рунета

Отмечается 7 апреля. Именно в этот день в 1994 году международная организация ICANN (The Internet Corporation for Assigned Names and Numbers), которая занимается вопросами регламентирования отношений в мировом доменном пространстве, зарегистрировала для России домен .Ru. Кроме того, 7 апреля было подписано Соглашение «О порядке администрирования зоны RU». В этом году Рунету исполняется 26 лет.

День Криптографической службы России

Свой профессиональный праздник отечественные шифровальщики отмечают 5 мая. По информации Центра общественных связей ФСБ, служба, созданная постановлением Совета народных комиссаров РСФСР от 5 мая 1921 года, обеспечивает с помощью шифровальных (криптографических) средств защиту информации в информационно-телекоммуникационных системах и системах специальной связи в РФ и ее учреждениях за рубежом, в том числе в системах, использующих современные информационные технологии.

Всемирный день информационного сообщества

ООН считает этот день праздником для всех представителей ИТ-сообщества. Генеральная Ассамблея ООН в 2006 году приняла резолюцию, в которой провозгласила 17 мая профессиональным праздником всех программистов, системных администраторов, интернет-провайдеров, веб-дизайнеров, редакторов интернет-изданий и всех остальных людей, занятых в сфере информационных технологий. До 2006 года этот праздник отмечался как Международный день электросвязи или Всемирный день телекоммуникаций. Дело в том, что 17 мая 1865 года в Париже был основан международный Телеграфный Союз.

День оптимизатора Рунета (День SEO-оптимизатора)

В этот день, 28 мая, свой профессиональный праздник отмечают оптимизаторы Рунета, или SEO-администраторы. Правда, праздник пока не имеет официального статуса, а о его существовании знают, в основном, специалисты данной сферы. Аббревиатура SEO (Search Engine Optimization) обозначает различные методы работы с поисковыми системами — с целью роста позиций ресурса в поисковой выдаче по определенным запросам пользователей. Профессия SEO-оптимизатора возникла в России еще в середине 90-х годов прошлого века. Именно тогда появились первые поисковые системы, позиции сайта в которых напрямую коррелировали с доходами от ресурса. С тех пор прошло много лет. Поисковики не раз меняли свои алгоритмы, параллельно с этим развивалось и SEO. Сегодня SEO — это мощная индустрия с многомиллионными оборотами.

День краудфандинга

7 июня 2015 года в России отметили новый праздник – День краудфандинга. Дата праздника была выбрана не случайно, именно 7 июня в 2012 году официально начал работу один из первых российских краудфандинговых сервисов – сайт Planeta.ru. Напомним, что краудфандинг (от англ. сrowd funding, сrowd – «толпа», funding – «финансирование») – это народное финансирование, или коллективное сотрудничество людей, которые добровольно жертвуют/вкладывают свои деньги, как правило через Интернет, чтобы оказать финансовую поддержку какому-либо проекту или организации. На русский же манер краудфандинг можно дословно перевести как – «с миру по нитке».

День системного администратора

Идея праздника пришла в голову сисадмина из Чикаго Теда Кекатоса (Ted Kekatos). Впервые он отмечался 28 июля 2000 года. Кстати, в 2000 году Папа Римский Иоанн Павел II официально назвал Святого Исидора покровителем пользователей компьютеров и интернета. Празднуется День сисадмина в последнюю пятницу июля. В этом году он отмечался 30 числа. Например, в России с 2006 года под Калугой ежегодно проходит Всероссийский слет системных администраторов, с каждым годом собирающий все больше и больше участников. Так, если первый слет посетило около 350 человек, то в 2009 году его участниками стали более 4000 человек из 174 городов России, Украины, Белоруссии и Казахстана.

День тестировщика

Отмечается 9 сентября. В этот день в 1945 году ученые Гарвардского университета, тестировавшие вычислительную машину Mark II Aiken Relay Calculator, нашли мотылька, застрявшего между контактами электромеханического реле. С тех пор именно эта дата считается профессиональным праздником людей, которые все свое время проводят в поисках багов, уязвимостей, «глюков» и прочих неполадок в ПО.

День программиста

Профессиональный праздник программистов, отмечаемый на 256-й день года (для программиста это 255-й день года или 0xFF-ный в 16-ричной системе счисления, так как счет начинается с нуля). Число 256 (28) выбрано потому, что это количество чисел, которое можно выразить с помощью восьмиразрядного байта. Отмечается праздник по предложению российского программиста Валентина Балта, сотрудника веб-студии «Параллельные Технологии», который еще в 2002 году собирал подписи под обращением к правительству РФ в поддержку признания этого дня официальным праздником.

В России праздник стал официальным только 11 сентября 2009 года, когда президент Дмитрий Медведев подписал Указ, подготовленный Министерством связи и массовых коммуникаций Российской Федерации, который устанавливает в стране новый официальный праздник — День программиста.

День рождения «смайла»

Это произошло 19 сентября 1982 года, когда профессор Университета Карнеги-Меллона Скотт Фалман (Scott E. Fahlman) впервые предложил использовать три символа, идущие подряд двоеточие, дефис и закрывающую скобку для обозначения «улыбающегося лица». Теперь это сочетание символов используется при онлайн-общении во всем мире, причем «отправить смайлик» могут друг другу не только друзья или знакомые, но и коллеги, а иногда улыбающееся лицо можно увидеть в диалогах между подчиненным и начальником.

Международный День интернета

Этот праздник предлагали сделать официальным несколько раз в разное время. Однако ни одна из предложенных дат так и не стала традиционной. Что касается России, то на неофициальном уровне Днем интернета считается 30 сентября. Дело в том, что с такой инициативой выступила компания из Москвы «IT Infoart Stars», которая разослала фирмам и организациям предложение поддержать их инициативу, состоящую из двух пунктов: назначить 30 сентября «Днем интернета», ежегодно его праздновать и провести «перепись населения русскоязычного интернета». На тот момент количество пользователей Рунета достигло 1 млн. человек.

Всемирный день юзабилити (World Usability Day)

был учрежден в 2005 году по инициативе Ассоциации Профессионалов Юзабилити. В этом же году он был впервые отпразднован и с тех пор отмечается ежегодно во второй четверг ноября. Юзабилити (Usability) можно дословно перевести с английского языка как «практичность, удобство и простота использования». Разумеется, существуют более подробные и точные определения. Например, Джейкоб Нильсен определяет юзабилити как: «… качество работы пользователя в некоторой интерактивной среде (web-сайт, программа и пр.)». Сегодня же термин «юзабилити» всё чаще используется как синоним слова «эргономичность» в контексте таких продуктов, как бытовая электроника или средства связи. В более широком значении он может также употребляться для определения степени эффективности выполнения механическими объектами и инструментами — такими, например, как дверная ручка или молоток — предписанных им функций. Таким образом, наиболее адекватным переводом слова «usability» будет «удобство и простота использования, применения» и даже «практичность». Одна из тем Дня прошлых лет была посвящена транспорту, которым мы пользуемся. Миссия этого праздника — повышение общественного сознания о необходимости упрощения доступа и повышения простоты использования продуктов и услуг, имеющих особо важное значения для человечества.

Всемирный день информации

Отмечается 26 ноября по инициативе Международной академии информатизации (МАИ), имеющей генеральный консультативный статус в Экономическом и Социальном советах ООН. Любой человек постоянно имеет дело с информацией, поэтому этот день по праву можно считать профессиональным праздником всех ИТ-специалистов.

Международный день защиты информации

Данный праздник отмечается с 30 ноября 1988 года по инициативе американской Ассоциации компьютерного оборудования. Цель праздника заключается в напоминании всем о необходимости защиты компьютерной информации, обратив внимание производителей и пользователей аппаратных и программных средств на проблему безопасности. Именно в 1988 году была зафиксирована первая массовая эпидемия компьютерного вируса. Это был червь, получивший название в честь своего автора Морриса.

День рождения отечественной информатики

В августе 1948 года член-корреспондент АН СССР Исаак Брук совместно с инженером Баширом Рамеевым представил проект автоматической вычислительной машины. А 4 декабря 1948 года Государственный комитет Совета Министров СССР по внедрению передовой техники в народное хозяйство зарегистрировал это изобретение за номером 10475 под названием «Цифровая электронная вычислительная машина»

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

Источник информации: [здесь]

Советы студентам Computer Science

Advice for Computer Science College Students by Joel Spolsky

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

Большинство студентов, к счастью, достаточно дерзки и не стесняются задавать вопросы старейшинам компьютерных наук, и это правильно, несмотря на то, что старики склонны говорить странные допотопные вещи типа: «спрос на операторов перфоратора превысит 100 млн. к 2010 году» или «lisp-карьера — круто именно сейчас».
Читать далее «Советы студентам Computer Science»

В поисках великих девелоперов

Где все эти великие девелоперы

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

По мере их изучения Вы отмечаете — «Хм-м-м-м, это может сработать», «Нет! Ни за что!» или «Интересно, смогу ли я убедить его переехать в Буффало». Но то, что точно никогда не случится, гарантирую, просто потому, что этого не может быть никогда, Вы не скажете себе — «Восхитительно! Он великолепен! Этот бриллиант мы обязаны заполучить!». На самом деле, Вы можете просмотреть тысячи резюме, если, конечно, знаете, как их читать, а это не так просто, и я к этому вернусь в пятницу. Вы можете изучить тысячи заявлений на работу и никогда не встретить великого девелопера [1]. Ни одного.
И вот почему. Великие девелоперы — это действительно лучшие люди в своем деле, и их попросту нет на рынке труда. В среднем, великий девелопер за все свою карьеру, возможно, раза четыре и сменит место работы.
Читать далее «В поисках великих девелоперов»