понедельник, 27 декабря 2010 г.

Boost spirit

Использование boost::spirit - Очень интересный способ парсинга строк. Приведу краткий пример:
#include <string>
#include <map>
#include <iostream>

#include <boost/spirit/core.hpp>
#include <boost/spirit/actor/assign_actor.hpp>
#include <boost/spirit/actor/insert_at_actor.hpp>
#include <algorithm>

using namespace boost::spirit;

bool parseConfig( const char * str, std::map<std::string,std::string> & mp ) {
    mp.clear();
    std::string key;

    return parse(str,
        ((lexeme_d[+alnum_p][assign_a(key)] >> '=' >> lexeme_d[+alnum_p][insert_at_a(mp,key)]) % ','),
        space_p
    ).full;
}

void show_elem(const std::pair<std::string,std::string> & pair) {
    std::cout << pair.first << " = " << pair.second << std::endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::map<std::string,std::string> map;

    std::cout << parseConfig(" key1=val1, key2=val2, param=param_val ", map) << std::endl;
    std::for_each(map.begin(), map.end(), show_elem);

    return 0;
}
Colored with dumpz.org

Пример будет дополняться объяснениями.

Очень хорошо объяснено тут:
http://blog.alno.name/ru/2008/09/parsers-in-cpp-with-boost-spirit

пятница, 30 июля 2010 г.

OpenMP: простой пример параллелизации вычислений.

В Visual Studio 2005 необходимо включить поддержку OpenMP, после чего можно запустить такой простой пример (перемножение матриц):
#include <QtCore/QCoreApplication>
#include <QDebug>
#include <QTime>
#include "omp.h"

int main(int argc, char *argv[])
{
 QCoreApplication a(argc, argv);

 int ii, jj, kk;
 const int dim = 1000;
 int * arr = new int[dim*dim];
 int * arr1 = new int[dim*dim];
 int * arr2 = new int[dim*dim];

 QTime time;
        time.start();

 for (ii = 0; ii < dim; ii++) {
  for (jj = 0; jj < dim; jj++) {
   for (kk = 0; kk < dim; kk++) {
    arr[ii, jj] = arr1[ii, kk] * arr2[kk, jj];
   }
  }
 }

 qDebug() << QString("Without OpenMP, time %1 sec").arg((double)time.elapsed()/1000);

 time.restart();

 #pragma omp parallel for shared(arr, arr1, arr2, dim) private(ii, jj, kk)
 for (ii = 0; ii < dim; ii++) {
  for (jj = 0; jj < dim; jj++) {
   for (kk = 0; kk < dim; kk++) {
    arr[ii, jj] += arr1[ii, kk] * arr2[kk, jj];
   }
  }
 }

 qDebug() << QString("With OpenMP, time %1 sec").arg((double)time.elapsed()/1000);

 delete [] arr;
 delete [] arr1;
 delete [] arr2;


 return a.exec();
}

Colored with dumpz.org


То есть перемножали матрицу без использования OMP и с использованием OMP. На выходе было (E7400 2.8GHZ):

"Without OpenMP, time 5.406 sec"
"With OpenMP, time 2.734 sec"

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

Тут можно почитать об этом чуть подробнее:
http://software.intel.com/ru-ru/articles/getting-started-with-openmp/

понедельник, 5 июля 2010 г.

Чтение с помощью STL

Очень интересный пример чтения файла с помощью STL:
http://easy-coding.blogspot.com/2009/02/stl.html

Любопытно скорость чтения последними двумя способами:

TEST(ReaderTest, istreambuf_iterator_tostring_short) {
  std::string v(
    (std::istreambuf_iterator<char>(
      std::ifstream(Env::testfile())
    )), 
    std::istreambuf_iterator<char>()
  );

  EXPECT_EQ(Env::testfile_sz(), v.length()); 
}


TEST(ReaderTest, istreambuf_iterator_tostring_short_auto_ptr) {
  std::string v(
    (std::istreambuf_iterator<char>(
      *(std::auto_ptr<std::ifstream>(
        new std::ifstream(Env::testfile())
      )).get()
    )), 
    std::istreambuf_iterator<char>()
  );

  EXPECT_EQ(Env::testfile_sz(), v.length()); 
}

Colored with dumpz.org


Если в VS2005 запускать без оптимизации, то каждый из этих двух тестов выполняется за 125 мс, а с оптимизацией - за 31 мс. То есть имеем четырехкратный прирост производительности.

четверг, 17 июня 2010 г.

STL: for_each и sort

В сети много примеров работы с STL, но часто в качестве шаблонного типа используется int или еще какой-то простой тип. Хотя довольно часто все не так тривиально и приходится хранить и сортировать достаточно сложные классы. Приведу небольшой пример. Допустим, у нас есть двухвалютный банковский счет с деньгами, причем сумма, понятно, зависит от текущего курса каждой из валют (доллар и евро). Чтобы упорядочить множество таких счетов, нужно учесть зависимость от текущего курса. Продемонстрируем это в коде:
#include <QtCore/QCoreApplication>
#include <QDebug>
#include <iostream>
#include <algorithm>
#include <list>
#include <math.h>

using namespace std;

void printInt(int numb);

//класс нашего банковского "кошелька"
class Wallet {
private:
  string owner;
  double eur;
  double usd;
public:
  //в конструкторе указываем дополнительные параметры
  //в данном случае это курсы валют относительно гривны
  Wallet(string owner, double usd, double eur) {
    this->owner = owner;
    this->eur = eur;
    this->usd = usd;
  };
  //как показывает практика, модификатор const тут необходим
  //(см. пояснения к коду)
  double getUsd() const { return usd; };
  double getEur() const { return eur; };
  void showInfo() {
    cout << "Wallet of " << owner << " contains a " << usd << " USD and " << eur << " EUR " << endl;
  };
};

void printWallet(Wallet & wallet) {
  wallet.showInfo();
};

//функтор для вывода информации
class WalletUahLess {
private:
  double uahToUsd;
  double uahToEur;
public:
  WalletUahLess(double uahToUsd, double uahToEur) {
    this->uahToUsd = uahToUsd;
    this->uahToEur = uahToEur;
  };
  //это обычный вариант сравнения двух "уошелкьков"
  bool operator() (Wallet & wallet1, Wallet & wallet2) {
    double uah1 = wallet1.getUsd()*uahToUsd + wallet1.getEur()*uahToEur;
    double uah2 = wallet2.getUsd()*uahToUsd + wallet2.getEur()*uahToEur;
    return uah1 < uah2;
  };
  //также бывает необходим и вариант с модификаторами const
  bool operator() (const Wallet & wallet1, const Wallet & wallet2) {
    double uah1 = wallet1.getUsd()*uahToUsd + wallet1.getEur()*uahToEur;
    double uah2 = wallet2.getUsd()*uahToUsd + wallet2.getEur()*uahToEur;
    return uah1 < uah2;
  };
};

int main(int argc, char *argv[])
{

  QCoreApplication a(argc, argv);

  vector<Wallet> wallets;

  //заполняем контейнер
  wallets.push_back(Wallet("Vasya", 960.50, 780.3));
  wallets.push_back(Wallet("Vova", 954.2, 789.56));
  wallets.push_back(Wallet("Piotr", 961.80, 792.67));
  wallets.push_back(Wallet("Ivan", 970.34, 760.50));
  wallets.push_back(Wallet("Sidor", 1090.76, 700.90));

  cout << endl << "before sort(): " << endl;
  for_each(wallets.begin(), wallets.end(), printWallet);

  //сортируем при одном курсе
  std::sort(wallets.begin(), wallets.end(), WalletUahLess(8.5, 9.6));
  cout << endl << "after sort() with WalletUahLess(8.5, 9.6): " << endl;
  for_each(wallets.begin(), wallets.end(), printWallet);

  //сортируем при другом курсе
  sort(wallets.begin(), wallets.end(), WalletUahLess(7.8, 11.5));
  cout << endl << "after sort() with WalletUahLess(7.8, 11.5): " << endl;
  for_each(wallets.begin(), wallets.end(), printWallet);

  return a.exec();
}

void printInt(int numb) {
  cout << numb << endl;
}

* This source code was highlighted with Source Code Highlighter.

На выходе консоли должно быть это:

before sort():
Wallet of Vasya contains a 960.5 USD and 780.3 EUR
Wallet of Vova contains a 954.2 USD and 789.56 EUR
Wallet of Piotr contains a 961.8 USD and 792.67 EUR
Wallet of Ivan contains a 970.34 USD and 760.5 EUR
Wallet of Sidor contains a 1090.76 USD and 700.9 EUR

after sort() with WalletUahLess(8.5, 9.6):
Wallet of Ivan contains a 970.34 USD and 760.5 EUR
Wallet of Vasya contains a 960.5 USD and 780.3 EUR
Wallet of Vova contains a 954.2 USD and 789.56 EUR
Wallet of Piotr contains a 961.8 USD and 792.67 EUR
Wallet of Sidor contains a 1090.76 USD and 700.9 EUR

after sort() with WalletUahLess(7.8, 11.5):
Wallet of Ivan contains a 970.34 USD and 760.5 EUR
Wallet of Vasya contains a 960.5 USD and 780.3 EUR
Wallet of Vova contains a 954.2 USD and 789.56 EUR
Wallet of Sidor contains a 1090.76 USD and 700.9 EUR
Wallet of Piotr contains a 961.8 USD and 792.67 EUR

Комментировать особо нечего, но стоит добавить, что использовать класс типа WalletUahLess вместо функции - это намного более гибкий и удобный способ сортировки, чем с помощью функции, т.к. позволяет задать дополнительные параметры - в данном случае различные курсы валют, не создавая при этом новый класс или функтор для сравнения.
В процессе выяснилась интересная вещь: на VS-2005 все замечательно работало, а на QT4 вдруг оказалось, что не хватает функции сравнения, где оба сравниваемых аргумента объявлены как const. После добавления перегруженной версии оператора (operator() (const Wallet, const Wallet &)) с константными объектами компилятор сообщил, что функции getUsd() и getEur() не могут вызываться в константном объекте, так как они сами не константные. После того, как они были объявлены константными, все заработало. Из чего можно сделать вывод, что реализации STL в разных компиляторах могут отличаться.
Также оказалось, что функция qSort() из библиоеки Qt работает так же, как и stl::sort() с объектами std::vector и не требует функции с const-параметрами.

вторник, 30 марта 2010 г.

Python

Питон - очень удобный и мощный скриптовый язык. Имеет красивый, легко читаемый синтаксис (это немаловажно).
Как всегда, хорошая статья на ibm.com:
http://www.ibm.com/developerworks/ru/library/l-python/?S_TACT=105AGX99&S_CMP=GR01

суббота, 27 марта 2010 г.

Обзор ЖК-мониторов

Обзор наиболее популярных технологий ЖК-мониторов (TN+Film, MVA, PVA, IPS), очень хорошая статья:
http://www.fcenter.ru/online.shtml?articles/hardware/monitors/10071

Детальный обзор мониторов Samsung F2080 и F2380:
http://www.fcenter.ru/online.shtml?articles/hardware/monitors/27237

Есть и куча обзоров других мониторов:
http://www.fcenter.ru/online.shtml?articles/hardware/monitors/14042

среда, 24 марта 2010 г.

Регулярные выражения

Регулярные выражения (Pattern, Matcher):
String input = "Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord.";
Pattern pattern = Pattern.compile("[A-Z][a-z]*([A-Z][a-z]*)+");
Matcher matcher = pattern.matcher(input);

while (matcher.find()) {
  System.out.println("Found this wiki word: " + matcher.group());
}


* This source code was highlighted with Source Code Highlighter.

На выходе должны быть:
Found this wiki word: WikiWord
Found this wiki word: AnotherWikiWord
Found this wiki word: SomeWikiWord
http://www.ibm.com/developerworks/ru/edu/j-intermed/section5.html

Парсинг дат

Парсинг дат (DateFormat, SimpleDateFormat):
try {
 // Some examples DateFormat
 DateFormat formatter = new SimpleDateFormat("MM/dd/yy");
 Date date = (Date)formatter.parse("01/29/02");
 formatter = new SimpleDateFormat("dd-MMM-yy");
 date = (Date)formatter.parse("29-Jan-02");
 // Parse a date and time; see also
 //Parsing the Time Using a Custom Format
 formatter = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss");
 date = (Date)formatter.parse("2002.01.29.08.36.33");
 formatter = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z");
 date = (Date)formatter.parse("Tue, 29 Jan 2002 22:14:02 -0500");
} catch (ParseException e) { }


* This source code was highlighted with Source Code Highlighter.
http://www.exampledepot.com/egs/java.text/ParseDate.html

пятница, 26 февраля 2010 г.

среда, 10 февраля 2010 г.

Производительность java-программ

Производительность java-программ можно увеличить, если применить серверные настройки (которые не так сильно экономят память, как настройки по умолчанию):
java -server MyApp
Если верить публикациям в интернете, то производительность может иногда увеличиться в два раза.

вторник, 9 февраля 2010 г.

Swing и многопоточность

Для прорисовки интерфейса в Java с помощью библиотеки swing лучше пользоваться, например, SwingUtilities.invokeLater(Runnable r), а не обычно создавать поток и запускать в нем Runnable. В противном случае будут лезть непонятные ошибки типа JTable Exception in thread "AWT-EventQueue-0" (ArrayIndexOutOfBoundsEx...) или подобные. Это происходит потому, что обращаться к swing-компонентам можно только из потока Event Dispatch Thread (EDT). Функцияция invokeLater ставит задачу в очередь на потоке EDT (т.к. все взаимодействие с swing-овскими графическими элементами управления необходимо осуществавлять только в потоке EDT). Но это не работает в случае, если необходимо выполнить длительный процесс, который заставит ждать поток EDT. Для таких случаев необходимо вместо обычного Thread задействовать SwingWorker.
Очень хорошая русскоязычная статья по этой теме:
http://developers.sun.ru/content/view/159/89/
и еще одна статья (видимо, слегка устаревшая):
http://www.ibm.com/developerworks/ru/edu/j-medswing/section4.html

Очень важно понять, что никаких обращений к swing-компонентам внутри метода SwingWorker.doInBackground() быть не должно т.к. он выполняется в отдельном потоке (не EDT) и если обращаться к swing-компонентам не из EDT потока, то это может стать причиной возникновения исключений. Вывод результатов можно производить, например, в методе SwingWorker.done(), который выполняется в потоке EDT и автоматически вызывается после того, как doInBackground() завершит свою работу, что оберегает нас от возможных проблем.

среда, 3 февраля 2010 г.

Процедуры и триггеры в Oracle

Процедуры
Ниже приведен пример кода процедуры. Сампример немного громоздок, т.к. взят непосредственно из рабочей базы без каких-либо модификаций и упрощений:
create or replace procedure jlb_set_kr_by_phone
is
 is_region_found NUMERIC(1,0);
 pers sch2.jlb_pays_temp2.account%TYPE;
 vcode_count NUMERIC(5,0);
 valid_vcode NUMERIC(5,0);
begin
 --перебираем лицевые
 FOR c1 IN (SELECT phone
    FROM sch2.jlb_pays_temp2
    WHERE phone IS NOT NULL AND account IS NULL) LOOP
   
  is_region_found := 0;
  vcode_count := 0;
  --ищем код района лицевого счета
  FOR c2 IN (SELECT
      z.vcode,
      c.npers
     FROM
      BS.BSSUBSACCT SA,
      bs.bstechnic t,
      bs.bstechsubs ts,
      bs.bsclient c,
      bs.vbszone z
     WHERE
      sa.nsubs = c.nclient_id
      and ts.nsubs=sa.nsubs
      and t.ntech_id=ts.ntech
      and t.vcode = c1.phone
      and c.nzone = z.nzone_id) LOOP
   --признак физ. или юр. лица  
   pers := c2.npers;
   --запомним найденный код р-на
   valid_vcode := c2.vcode;  
   --укажем, что код р-на такого лицевого был найден
   is_region_found := 1;
   --считаем кол-во найденных р-нов    
   vcode_count := vcode_count + 1; 
  END LOOP; --end c2

  --если все в порядке
  IF is_region_found = 1 AND vcode_count = 1 AND pers IS NOT NULL THEN
   UPDATE sch2.jlb_pays_temp2 p
   SET p.is_invalid = 0, p.reg_code = valid_vcode
   WHERE p.phone = c1.phone;
  END IF;
  --если не найдено кодов района (т.е. телефон не найден ни в одном из р-нов)
  IF is_region_found = 0 THEN
   UPDATE sch2.jlb_pays_temp2 p
   SET p.is_invalid = 6
   WHERE p.phone = c1.phone;
  END IF;
  --если неск. кодов района - тоже ошибка
  IF is_region_found = 1 AND vcode_count > 1 THEN
   UPDATE sch2.jlb_pays_temp2 p
   SET p.is_invalid = 7
   WHERE p.phone = c1.phone;
  END IF;
  --если это юрлицо
  IF pers IS NULL THEN
   UPDATE sch2.jlb_pays_temp2 p
   SET p.is_invalid = 1
   WHERE p.phone = c1.phone;
  END IF;
 
 END LOOP; --end c1

 commit;

end jlb_set_kr_by_phone;


* This source code was highlighted with Source Code Highlighter.
В вышеприведенном примере, если говорить коротко, в таблице выставляются соостветствующие телефонам коды районов.

Триггеры
Иногда достаточно удобно использовать триггеры. Допустим, у нас есть таблица файлов платежей, у записей которой есть потомки - платежи, хранящиеся в таблице платежей. Необходимо, чтобы при удалении файла автоматически удалялись и все платежи, связанные с ним. Ниже представлен простой пример:
--таблица файлов
create table jlb_files(
 file_id number(10) PRIMARY KEY USING INDEX,
 bank_id number(10),
 file_name varchar2(50),
 file_date DATE,
 upload_date DATE,
 total_pays_cnt NUMERIC(9, 0),
 total_pays_sum NUMERIC(10, 2)
);

--таблица оплат
create table jlb_pays(
 pay_id number(10) PRIMARY KEY USING INDEX,
 file_id number(10),
 account varchar2(20),
 pay_sum NUMERIC(9,2),
 pay_date DATE,
);

--триггер
CREATE OR REPLACE TRIGGER jlb_files_before_delete
BEFORE DELETE
ON jlb_files
FOR EACH ROW
DECLARE
BEGIN
 DELETE FROM jlb_pays p
 WHERE :old.file_id = p.file_id;
END;


* This source code was highlighted with Source Code Highlighter.
Очевидно, что jlb_files - это таблица файлов, а jlb_pays - это таблица дочерних платежей. При удалении файла из jlb_files автоматически удаляются все платежи с соответствующим file_id. В теле триггера видно, что old представляет собой удаляемую строку.
http://www.techonthenet.com/oracle/triggers/before_delete.php

понедельник, 4 января 2010 г.

Windows PowerShell

Очень удобное средство в Windows для написания различных скриптов, автоматизирующих работу. Более мощное средство, чем стандартные cmd-скрипты. Файлы powershell-скриптов имеют расширение ".ps1". Вот пример простенького скрипта, в котором мы архивируем файлы с помощью 7-zip и копируем их в архивную папку:
# archive files using 7-zip
$application = "C:\Program Files\7-zip\7z.exe"
$arguments = "a -tzip C:\xls.zip -r  F:\results\xls\*.*"
$process = [Diagnostics.Process]::Start($application, $arguments)
do { } while (!$process.HasExited)
# create new folder and copy file
$curdate = get-date -format "yyyyMMdd"
$src = "C:\xls.zip"
$dest = "D:\archives\$curdate"
New-Item $dest -type directory -force
Copy-Item $src "$dest\xls.zip"
Стоит особо пояснить строку do {  } while (!$process.HasExited). Здесь мы проверяем в цикле состояние запущенного процесса и скрипт не продолжит выполнятся, пока процесс создания файла архива полностью не завершится. Вместо этой конструкции можно использовать
$process.WaitForExit()
или вообще все в одну строчку лаконично уместить:
[Diagnostics.Process]::Start($application, $arguments).WaitForExit()

Инода возникает необходимость узнать дату, которая, предшествующую текущему времени и использовать ее, допустим, в названии архивной папки. Это можно сделать, например, таким образом:
# get date before 30 days ago
$date = (Get-Date).AddDays(-30)
$datestr= "{0:yyyyMM}" -f $date
$path = "D:\archives\$datestr"
В результате переменная $path будет чем-то вроде  "D:\archives\200912".
Удобно работать с файлами. Следующий скрипт, например, выводит список файлов и их размер:
foreach ($file in Get-ChildItem "F:\old_bases\DBF")
{
   $file.name + " " +$file.length
}
В нижеприведенном примере происходит копирование файлов из одних папок в другие при помощи цикла foreach:
foreach ($depNo in 40..75)
{
   if(($depNo -gt 66) -and ($depNo -lt 72)) { continue }
   $src = "F:\resultxls\dep" + $depNo + "\tables"
   $dst = "T:\backup\dep" + $depNo+ "\tables"
   Copy-Item "$src\*.xls" -destination "$dst"
  
   "files to dep$depNo are copied"
}
Что интересно, запуск скриптов можно осуществлять как набрав в консоли паэршелла полный путь скрипта типа "D:\scripts\ps1scripts\test.ps1", так и "D:\scripts/ps1scripts/test.ps1". Причем при указании пути во втором случае при указании диска используется обратный слеш, а при указании пути прямой. Также можно в консоли пауэршелла применить команду cd и сделать нужную папку со скрипатми текущей, и вызвать из ее корня необходимый скрипт, набрав "./script.ps1". То есть точка и прямой слеш подразумевают текущую директорию.

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