Windows Dev. Site

Haskell from Excel

HaskellのモジュールをExcelから呼び出す方法がかかれたBlogをみつけたので、実際に自分の環境でやってみました。

Calling Haskell from Excel

http://flxldn.tumblr.com/post/163596541/calling-haskell-from-excel

しかし環境が違うためか、残念ながらできませんでした。せっかくなので覚えとして書きたいと思います。
(いろいろやってみてXLLアドインの勉強にはありましたが・・)

C++からHaskellの呼び出し、ExcelからのC++(XLL)呼び出しはできますが、つなげると以下のようなアラートがでました。
xllError
XLLは、xlw(A Wrapper for the Excel API)を使う方法と、Excel 2010 XLL SDKを使う方法と、二通りやりました。

参考)
xlw :

http://xlw.sourceforge.net/

http://d.hatena.ne.jp/teramonagi/20110124/1295866787

xll sdk:

http://www.microsoft.com/en-us/download/details.aspx?id=20199

テスト環境 Excel 2010 + xlw ver5, Visual Studio 2010 / Windows 7

まずHaskellソースからDLLを作成します。
addr.hs

module Adder where 

adder :: Int -> Int -> IO Int --gratuitous use of IO 
adder x y = return (x+y) 

foreign export stdcall adder :: Int -> Int -> IO Int

adder.h

#ifdef __cplusplus
extern “C”
{
#endif
__declspec(dllexport) void __stdcall HsBegin(void);
__declspec(dllexport) void __stdcall HsEnd(void);
__declspec(dllexport) long __stdcall adder(long x, long y);
#ifdef __cplusplus
}
#endif

adder.def

LIBRARY Adder
EXPORTS
adder@8=_adder
HsBegin@0=_HsBegin
HsEnd@0=_HsEnd

dllMain.c

static char* args[] = {"ghcDll" , NULL };

BOOL STDCALL DllMain( HANDLE hModule, DWORD reason, void* reserved)
{  return TRUE;}
__stdcall void HsBegin()
{
startupHaskell(1, args, __stginit_Adder);
}
__stdcall void HsEnd()
{
hs_exit();
}

以下コンパイル手順です。(VisualStudio2010コマンドプロンプト)

ghc -static -c adder.hs -fglasgow-exts
ghc -static -c dllMain.c
ghc -static -shared -o adder.dll adder.o adder_stub.o dllMain.o
lib /MACHINE:x86 /DEF:adder.def /OUT:adder.lib /NOLOGO /SUBSYSTEM:WINDOWS

出力されたadder.dllはSystem32フォルダにコピーします。

次に、XLLファイルを作成します。
xlwのインストールディレクトリの中にあるxlwTemplateExtractor.exe実行すると、XLL_Projectというフォルダがマイドキュメントに作成されるので、ここにあるソリューションを開きます。
デフォルトのEchoShortの下に追加して以下のようになります。

#include<cppinterface.h>

#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllexport) void __stdcall HsBegin(void);
__declspec(dllexport) void __stdcall HsEnd(void);
__declspec(dllexport) long __stdcall adder(long x, long y);
#ifdef __cplusplus
}
#endif

#pragma warning (disable : 4996)

short // echoes a short
EchoShort(short x // number to be echoed
           )
{
    return x;
}

short funcAdd(short d1, short d2){
 return adder(d1, d2);
//return d1 + d2;
}

cppinterface.hにはfuncAddのプロトタイプを追加します。
xlwWrapper.cppというソースは自動で生成されるようです。
adder.libを参照するようにします。

まずコメントしてあるd1+d2で成功したことを確認してから、adderのテストしました。
すると上記アラートがでます。
ここで、HsBegin、HsEndを使い方を元のブログを見てみると、ライブラリ自体のmakeが必要のようだったのであきらめ、次にXLL SDKをテストしました。(ライブラリを変更してもたぶん結果は同じという気もしましたし)

XLL SDKをインストールしたフォルダ内にある、EXAMPLEプロジェクトを使用します。
他の関数を参考にして、


#define rgFuncsRows 30

static LPWSTR rgFuncs[rgFuncsRows][7] = {

....
	{L"CalcCircum",					L"BB", L"CalcCircum"},
	{L"funcAdd",					L"III", L"funcAdd"}
};

....
__declspec(dllexport) double WINAPI CalcCircum(double pdRadius)
{
	return pdRadius * 6.283185308;
}
__declspec(dllexport) short WINAPI funcAdd(short x, short y)
{
	return (short)adder((long)x,(long)y);
	//return x + y;
}

このソースのxAutoOpen()、xAutoClose()にそれぞれHsBegin()、HsEnd()を追加。これらとadderの宣言部もxlwと同様に追加。

これもreturn x+yで動作確認後、Haskell呼び出し部をテストしました。それで上記アラートが出ました。
Excel-DNAてadder.dllを、とも思いましたが、あれはマネージDLLでした。
成果が何もないのもなんなので、cppコンソールアプリでHaskell呼び出しをしてみました。(adder.dllはexeと同じところに置く)

#include "stdafx.h"

#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllexport) void __stdcall HsBegin(void);
__declspec(dllexport) void __stdcall HsEnd(void);
__declspec(dllexport) long __stdcall adder(long x, long y);
#ifdef __cplusplus
}
#endif

int _tmain(int argc, _TCHAR* argv[])
{
	HsBegin();
	printf("adder : %d\n", adder(1,2));	
	HsEnd();	
	getchar();
	return 0;
}

3 を表示します。

また解決したらつづきを書きたいと思います。

C# – IronPython データ連携

C#とIronPythonを連携させようと思ったのは、Pythonの数値計算ライブラリNumPyがIronPythonでもインストールできるというサイトを見つけたからです。(また、VSTOでExcelのデータ処理にも使えればと)

https://www.enthought.com/repo/.iron/

これを使って、C#、IronPython間のデータの受け渡しテストをしてみました。

環境 : IronPython 2.7.3 (2.7.0.40) on .NET 4.0.30319.296 (32-bit), Visual Studio 2010 / Windows 7

インストール
1)ironpkg-1.0.0.pyをダウンロード
2)以下コマンド実行

ipy ironpkg-1.0.0.py –install
ironpkg scipy
ipy -X:Frames -c “import scipy”

いろいろと調べてトライしましたが、残念ながらSciPyは使えないようです。しかしNumPyは使えるので、こちらだけ使いました。
(NumPyだけできるものにしました)

csTest.py

import sys
sys.path.append(r'C:\Program Files (x86)\IronPython 2.7')
sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\DLLs')
sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib')
sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib\site-packages')

import clr
clr.AddReference('mtrand.dll')

import numpy as np

na = np.zeros(shape=(2,2))

for i in range(2):
	for j in range(2):		
		na[i, j] = mx[i, j]

mr = np.linalg.inv(na)
print '- Iron Python -'
print mr
print np.dot(na, mr)

for i in range(2):
	for j in range(2):
		mx[i, j] = float(mr[i, j])

C#で設定した2次元配列のデータをNumPyを使って逆行列の計算をして戻すというものです。
しかしC#の配列からのNumPy配列に渡す(その逆も)やり方ついていろいろと悩みました。
mxのままだと逆行列を求める関数でエラーをだしました。(np.array(mx)とやるときは、mxはジャグ配列([][])でないといけないみたいだし・・)
結果代入という形にしてしまいましたが、もっといい方法がないかまた調べたいと思います。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using System.Collections;
using IronPython.Runtime;

namespace csPyTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var pe = Python.CreateEngine();           
            ScriptSource src = pe.CreateScriptSourceFromFile(@"C:\csTest.py");
            ScriptScope scope = pe.CreateScope();

            double[,] mx = new double[,] {{0,1},{2,3}};
            scope.SetVariable("mx", mx);

            src.Execute(scope);

            var m = scope.GetVariable<double[,]>("mx");

            Console.WriteLine(" - C# - ");
            for (int i = 0; i < 2; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    Console.Write(m[i, j] + " ");
                }
                Console.WriteLine();
            }
            Console.ReadLine();
        }
    }
}

C#からダイナミック言語のPythonの変数に直接アクセスできるのも、強力な機能ですね。しかしもうすこしCPythonのライブラリが使えるようになってほしいところです。

以下は実行結果です。逆行列の確認として、単位行列を出力しています。
NumPy

Pyvot / Excel

Excelネタが続きますが、今回はPythonを使ったExcel操作です。Excel「ファイル」への読み書きを目的にしたものではなく、開いたままのシートに対してリアルタイムに読み書きするものです。Pythonはソースを見やすいようにパッケージの形にしましたが、コンソールでの操作を想定しています。
コンソール画面でプログラムのテストしたりするときに、標準入力や標準出力をセルでやると面白いかもということで試してみました。WindowsやLinuxなどのコマンドラインでデータの入出力をするときなど、ファイルでもいいのですが、データを部分的に変更したりするとき、この方法は便利です。視覚的にもわかりやすいです。(こんな用途かぎられるかもしれませんが・・)

環境 : Python 2.7.3, Excel 2010 / Windows 7
インストール :

http://pytools.codeplex.com/wikipage?title=Pyvot

PTVSからもできるようですが、よくわかりませんでしたので、

https://pypi.python.org/pypi/Pyvot

こちらのサイトから
Pyvot-0.1.2-py2.7.egg
をダウンロードして、easy_install コマンドを使いました。
あとwin32comも必要です。

使い方に関しては、以下の動画がわかりやすかったです。

基本的な機能を使ってみました。
pyvot_1
Pythonから書き込んだあと、Excelシートをエディトして(1 -> 10)、Excelから読みこみます。(w.get())
Pyvot_2
これを踏まえて、ちょっと変わった使い方ですが連続してデータをExcelに送り、グラフを表示してみました。

import xl
import threading
import time
import random

def main():
	xl.Workbook('xlTest.xlsx')
	
	r = range(1,11)
	for i in range(10):
		xl.get("A1:A10").set(r)
		random.shuffle(r)
		time.sleep(1)

if __name__ == '__main__':
	main()


初期値は以下で、
PYvot_3
あとは1秒ごとにランダムに並び順が変化します。
Pyvot_4
実は、マルチスレッドでデータを変化させ、リアルタイム解析みたいなことができないか、ということをしたかったのですが、Pythonのスレッドの中からxl関数を呼び出すと、エラーになってしまいました。また前提条件として、プログラム動作中、セルをクリックすると停止します。あと変更対象のセルにはあらかじめデータを入れておく必要があります。
自分が個人的にやりたかったことができないものもありましたが、十分使えるツールだと思います。

また、いろいろと試していきたいと思います。

Excel と R via VSTO

RExcelアドインを使ってExcelとR連携は前々回テストしましたが、VSTOを使ってRを呼び出すテストをしてみました。
これには、“C#, Python で R.NET”でテストしたR.NETを使います。
プロジェクトは“LINQ/VSTO”と同じ方法で作成します。

環境: Excel2010, R 3.0.0 / Windows 7
VisualStudio 2010 参照設定追加
RDotNet
RDotNet.NativeLibrary

using System;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using RDotNet;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelWorkbook3
{
    public partial class Sheet1
    {
        private void InternalStartup()
        {
            DataTable table = new DataTable();
            Excel.Worksheet activeSheet = ((Excel.Worksheet)Application.ActiveSheet);

            var envPath = Environment.GetEnvironmentVariable("PATH");
            var rBinPath = @"C:\Program Files\R\R-3.0.0\bin\i386";
            Environment.SetEnvironmentVariable("PATH", envPath + Path.PathSeparator + rBinPath);
            using (REngine engine = REngine.CreateInstance("RDotNet"))
            {
                engine.Initialize();

                StringBuilder plotCommmand = new StringBuilder();  
                plotCommmand.Append(@"rnorm(100)");
                var e = engine.Evaluate(plotCommmand.ToString());
                int k = 1;
                foreach (var v in e.AsNumeric().ToArray<double>())
                {
                    activeSheet.Cells[k, 1].Value = v;
                    k++;
                }
            }
        }
    }
}

R側では正規分布の乱数を生成するrnorm関数を使い、その出力をExcelシートに表示させます。Excel側では、グラフ表示をして正規分布を確認しました。(手動)
(ちなみにRのPlotを呼び出すこともできます)
R_VSTO1
このような単純な例では、どんな連携方法も大差ありませんが、その他に組み合わせるものがある場合、いろいろと解決方法を知っておくと便利です。

Excel Add-Ins in Python, PyXLL

前々回の投稿のExcel-DNAのホームページを見ていると、PythonでExcelと連携するPyXLLというものの存在をしりました。
Pythonが主でExcelシートを操作できるライブラリもありますが、Excelが主でPythonをバックエンドで使えるというメリットは、それ以上の広がりがあると思いテストしてみました。

環境 : PyXLL ver2.0.4, Excel 2010 / Windows 7
Download : http://www.pyxll.com/index.html

from pyxll import xl_func

@xl_func("var[] nnn: var[]")
def testFunc1(nnn):
	res = []
	for nn in nnn:
		r = []
		for n in nn:
			n = n * n
			r.append(n)
		res.append(r)
	return res
	

元のマトリックスの要素を自乗します。
pyxll1

Excel-DNAのときと同様、この状態で CTL + SHIFT + Enter を押します。

pyxll2

Excel-DNAの手軽さでPythonが使える、それだけでも魅力ですが、データ操作をPythonでできるというのは、生産性が高くなります。

Excel と R

前回に引き続きExcelと外部ツールの連携についての調査で、フリーの統計解析ツールとして有名なRを試してみました。
Rは以前R.NETの話題で投稿しましたが、とても強力なツールで時々使用しています。解析という点では、Excelだけで十分なことも多いのですが、作業環境といった点で使い分けるメリットは大きいと思っています。
連携ツールには、RExcelというアドインを使いました。

環境 : R version 3.0.0, Excel 2010 / Windows 7

1) 以下をインストールします
RExcel_3.2.13
statconnDCOM3.5-1B2_Noncommercial

2) Rでは、
rscproxy
rcom
をインストールします

http://rcom.univie.ac.at/download.html

3) ExcelのオプションからアドインRExcel2007を組み込みます
使用するExcelは2010ですが、これでも動作しました。(たまにエラーがでて不安定な気もしますが、一応動いています)

参考) http://d.hatena.ne.jp/Rion778/20091012/1255362015

これで、プレゼンテーションはExcel、バックグランドはRといった使い分けをした例をつくってみました。
RExcel5
RExcel7
テストの意図は、データスライダーでデータの分布をリアルタイムに変更させたものをRでデータと回帰分析結果をグラフ表示するといったものです。
回帰分析自体はExcelだけでもできますが、わかりやすい例ということでRでやってみました。
興味深い点は、以下のRのソースをエクセルで記述して、範囲指定して関数定義できます。それをRApply関数を通して実行できます。

function(x,y,L){
  xy <- data.frame(x=x,y=y)
  xy.lm <- lm(x~y,data=xy)
  plot(x, type="l",xlim=c(0,16),ylim=c(0,L),col="green",lwd=1)
  par(new=T)
  plot(y, xlim=c(0,16), ylim=c(0,L), col="red", pch=3)
  abline(xy.lm, col="blue", lwd=2)
}

これも含めて、いくつかのわかりやすいデモが用意されています。
RMenu1
今回はわざわざスライダーやブロットという見た目わかりやすいものを使いましたが、ExcelシートのデータをRにデータフレームという形で渡し、Rで演算した結果を地味にExcelに戻すというのは使えるのではと思いました。
RではExcelにないライブラリを使えるのも魅力ですが、Matlabのようにマトリックス演算を使って自前でいろいろプログラミングできるのは面白そうです。
最近このジャンルには、いろんな可能性を感じているため鋭意調査・勉強中ですが、やりたいことがたくさんあるのに時間が不足している状況です。少しづづ整理して、またまとめていきたいと思っています。

しかしながら、ExcelとRの書籍を比べてみると、Excelはカラーで数も多く価格も安い反面Rはそれとは逆の傾向にありますね。当然といえば当然ですが、通常の業務を考えてみてもExcelの普及率、利用率の高さにはとても驚かされる次第です。(仕様書読んだり作成したりとか多いですが・・)

Excel-DNA

Excelと外部ツールが連携できるものをいろいろと調べていたら、Excel-DNAというものが面白そうだったので試してみました。
ver 0.30 (Wed Dec 12, 2012 at 4:00 PM)

http://exceldna.codeplex.com/

また以下サイトに大変詳しく説明されています。

http://supermab.com/wp/

今回これを参考にさせていただきました。

ExcelDna-0.30を上記サイトからダウンロードしDistributionフォルダの以下のファイルをワークフォルダにコピーします。
ExcelDna.dna
ExcelDna.xll
この二つのファイルは同じフォルダに置き、拡張子以外のファイル名は変更してもいいですが同じものにします。
編集するのは.dnaファイルのみで.xllファイルはExcelへのインポート(ドラッグ&ドロップまたはアドイン追加)に使用します。

ExcelDna.dna

<![CDATA[
using System;
using System.Numerics;

public class Test
{
	public static object [] TestArray(int n)
	{
		int x = 10;
		object [] arr = new object[x];

		for(int i=0;i<x;i++){
 			arr[i] = n + i;
 		}
 		return arr;
 	} 
} 
]]>

C#のソースをコンパイルをすることなく、テキストエディタで編集するだけというのがとても手軽です。
そしてこれだけで、なんとTestArry()がユーザ定義関数として、Excelで利用できます。ちょっと驚きです。

私がやりたかった「ユーザ関数から配列を返した結果を複数のセルに表示させる」ことにはちょっと戸惑いました。
そもそもExcelの配列数式なるものを知らなかったためです。
あと、配列をint[]で返すとExcelから見えないようです。エラーをださないので原因がわかりませんでした。

以下に手順をメモしておきたいと思います。

excelDna1
上のような状態で、CTRL + SHIFT + Enter を押します。セルは複数選択しておく必要があります。
選択したセルが一つだったり、Enterキーだけ押した場合は、一つのセルにしか表示しません。
excelDna2
無事表示できました。^^;
個人的にはVSTOが好きですが、この手軽さはとても魅力に思いました。

Azure Storage Explorer & SharpDevelop

前々回のAzure Table Storage、前回のメタプログラミングの投稿で、補足しておきたいことがありましたので、ここにまとめて書きたいと思います。
Azure Table Storageは、LINQPad以外にも、Azure Storage Explorerという便利なツールがあったのでこれでもテストしてみました。

ver 4.0.0.10 (05.07.2011)

http://azurestorageexplorer.codeplex.com

AzureStorageExplorer1

ShapDevelopは、Boo言語が標準で対応しているのですね。以下のようにC#からコードがコンバートすることもできます。

ver 4.3.3 Build 9663 (.NET 4.0.30319.296)

http://sharpdevelop.codeplex.com/

sharpDevelop1
sharpDevelop2
sharpDevelop3
逆はできないようです。
ツールのメモでした。。

メタプログラミング.NET

Windowsストアアプリの開発をしていたところ、Windows8.1環境が複数いることになり、ちょっと心が折れてしまいました。来月正式版がでるようなので、それを待って再開したいと思います。

それもあり、ASCIIから出たタイトルと同名の本に興味深い内容があったのでテストしてみました。
メタプログラムという、プログラムをプログラムする機能を使って、一つのコードから複数の.NET言語コードを生成してみました。
C++/CLI, C#, VB.NETの出力ができることを確認しましたが、それだけではつまらないと思い、VJ#とかやってみようと思いましたが、どうやら最近のVisualStdudioでは対応していないようです。F#も対応していないようです。
そして、本を読み進めていくとBooという言語があることを知りました。Mono(.NET互換環境)とかでよく使われているPythonに似た言語ということで、面白そうなのでこれを出力する方法調べてみました。

https://github.com/bamboo/boo にある、bin/Boo.Lang.CodeDom.dll をダウンロードして参照に追加します。
環境 : VisualStudio 2010 / Windows 7

using System;
using System.IO;
using System.Text;
using System.CodeDom;
using System.Diagnostics;
using System.CodeDom.Compiler;

class TestCodeDOM
{
    static void Main()
    {
        CodeNamespace prgNamespace = BuildProgram();
        var compilerOptions = new CodeGeneratorOptions()
        {
            IndentString = "  ",
            BracingStyle = "C",
            BlankLinesBetweenMembers = false
        };
        var codeText = new StringBuilder();
        using (var codeWriter = new StringWriter(codeText))
        {
            CodeDomProvider provider = new Boo.Lang.CodeDom.BooCodeProvider();

            ICodeGenerator gen = provider.CreateGenerator();
            CodeGeneratorOptions options = new CodeGeneratorOptions();
            gen.GenerateCodeFromNamespace(prgNamespace, codeWriter, compilerOptions);
        }
        Console.WriteLine(codeText.ToString());
        Console.ReadLine();
    }
    static CodeNamespace BuildProgram()
    {
        var ns = new CodeNamespace("TestCode");
        var systemImport = new CodeNamespaceImport("System");
        ns.Imports.Add(systemImport);
        var programClass = new CodeTypeDeclaration("Program");
        ns.Types.Add(programClass);
        var methodMain = new CodeMemberMethod
        {
            Attributes = MemberAttributes.Static,
            Name = "Main"
        };
        methodMain.Statements.Add(
          new CodeMethodInvokeExpression(
            new CodeSnippetExpression("Console"),
            "WriteLine",
            new CodePrimitiveExpression("Hello world")
          )
        );
        programClass.Members.Add(methodMain);
        return ns;
    }
}

これを実行すると以下のようなコードを出力します。

namespace TestCode
import System

class Program:
static def Main():
Console.WriteLine(“Hello world”)

言語プロバイダの部分を変更すると、VBやC#、C++も出力することができます。

CodeDomProvider provider = CodeDomProvider.CreateProvider("cpp");

.NETはいろんなことできるものですね。
ちょっと禁断の領域に足を踏み入れたかも・・


#
C#
Tags:

Azure Table Storage / LINQPad

LINQPadAzure

LINQPadを使う機会があったので、ついでに以前、WindowsAzure Storage Python ライブラリの投稿でテストした、Azure Table StorageをLINQPadからアクセスしてみました。

http://blog.madd0.com/2012/01/09/linqpad-driver-for-azure-table-storage/

このサイトにある、TableStorageのドライバーのcompiled and packaged を入手して、LINQPadにDriver(Madd0.AzureStorageDriver-v1.0.0-beta.lpx)をインポートします。
(Add connection -> View more drivers -> Browse..)

ドライバーのおかげで簡単にアクセスできるようになりました。

環境: LINQPad Free Edition 4.45.05 / Windows 7

このようなちょっとしたテストをBlogに残すって結構便利なんですよね。Pythonで以前やったテストも、Blogを参考にして再テストしたりしましたし、日付や前後の記事からいつごろどういった背景でやったテストかがわかったりと、自分自身に役立ちます。ツールのバージョンやインターフェイスが変わって現在では同様のテストができなくても(とくにサービスからみとか)、エビデンスとして残っていると、どこまでできたかがよくわかります。

と、つくづく思ったテストでした。