スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#入門 他のアプリとの連携編 その1 Excel(1)

今回はExcelとの連携を紹介したいと思います。
 
まず、新しいソリューションで、新しいプロジェクトを作成しておきます。
(今回はF#コンソールアプリケーションとして作成してみました。)
まずは参照を追加します。
F#Interactiveでも利用したいので、ソースの先頭にを次のようにします。
 
#if INTERACTIVE
#r "office.dll"
#r "Microsoft.Office.Interop.Excel.dll"
#endif
 
open Microsoft.Office.Interop.Excel
 
上の部分をALT+retrunでF# Interactiveに送ると
 

 
--> Referenced 'C:\Program Files\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office14\office.dll'
 
--> Referenced 'C:\Program Files\Microsoft Visual Studio 10.0\Visual Studio Tools for Office\PIA\Office14\Microsoft.Office.Interop.Excel.dll'
 
と表示されます。
 
また、solution explorer側でも、参照の追加を利用して2つのDLLに参照を追加しておきます。
 
次にApplicationClassのインスタンスを生成して、appという識別子に束縛しておきます。
 
let app = new ApplicationClass(Visible = true)
 
この一行をF# Interactiveに送ると、workbookなしのExcelが起動されます。
 
次に、workbookを追加して、それをworkbookという識別子に束縛します。
 
let workbook = app.Workbooks.Add(XlWBATemplate.xlWBATWorksheet)
 
XlWBATemplateというのは、作成するworkbookのタイプを指定する為の型です。
 
上の一行をF# Interactiveに送ると、excelにworkbookが追加されます。
(ExcelをF#InteractiveからInteractiveに操作できる訳で、結構新鮮です。)
 
次に一枚目のsheetをworksheetという識別子に束縛します。
 
> let worksheet = workbook.Worksheets.[1];;
val worksheet : obj
 
見ての通り、これではobj型になるので、Worksheet型へキャストが必要です。
次のようにします。
 
let worksheet = workbook.Worksheets.[1] :?> Worksheet
 
これで準備が整いましたので、F#側からExcelのシートに値を書き込んでみたいと思います。
 
まずはセル単位で書き込む方法です。
 
セルの座標は左上を(1,1)として、(行row,列col)で考えます。
つまりc7セルはrow = 7,col = 3となります。
c7セルに値10を書き込むには次のようになります。
 
worksheet.Cells.Item(7,3) <- 10
 
これでc7セルの内容が10となります。
セルの値はobj型なので
worksheet.Cells.Item(7,3) <- box 10
としても同等です。
文字列も同様に書きこめます。
 
worksheet.Cells.Item(6,3) <- "Hello Excel"
 
helper関数として次のようなものを定義しておいてもよいかと思います。
 
> let set (ws:Worksheet) (row:int) (col:int) v =
    ws.Cells.Item(row,col) <- v;;
 
val set : Worksheet -> int -> int -> 'a -> unit
 
>  set worksheet 1 2 100;;
val it : unit = ()
 
でB1セルの値が100になります。
 
次に開始する行と列とseq型の値を指定すると、水平方向に書き込む関数を上のsetを利用して定義してみます。
 
> let setH (ws:Worksheet) (sRow:int) (sCol:int) (vSeq :seq<'a>) =
    vSeq 
    |> Seq.mapi (fun i v -> (sRow,sCol + i ,v)) 
    |> Seq.iter (fun (r,c,v) -> set ws r c v);;
 
val setH : Worksheet -> int -> int -> seq<'a> -> unit
 
 
> setH worksheet 2 1 [1;2;3;4;5];;
val it : unit = ()
 
とするとA1セルに1、B1セルに2、C1セルに3が書き込まれます。
 
同様に垂直方向に書き込む関数も定義してみます。
 
> let setV (ws:Worksheet) (sRow:int) (sCol:int) (vSeq :seq<'a>) =
    vSeq 
    |> Seq.mapi (fun i v -> (sRow+i,sCol,v)) 
    |> Seq.iter (fun (r,c,v) -> set ws r c v);;
 
val setV : Worksheet -> int -> int -> seq<'a> -> unit
 
 
列はA,B,C,D...Z,AA,AB,...という形式になっているので、
列(整数)-> 列名 という変換をする関数rToNという関数を定義してみます。
 
> let rToN (n :int) =
 
    let charArr = [|for c in 'A' .. 'Z' -> c|]
 
    let rec rToNSub rem result = //26進数もどきへの変換、0(="A")は[0],1(="B")は[1],26(="AA")は[0;0]となる
                                 
        if rem <= 25 then
            rem :: result
        else
            let red = rem % 26
            rToNSub ((rem - red)/26 - 1 ) (red :: result) // -1 がミソ 
    
    let makeUpCharArr =
        (rToNSub (n-1) []) // -1 は列が1から始まる為
        |>  List.map (fun i -> charArr.[i])
        |> Array.ofList
     
    new string(makeUpCharArr) ;;
 
val rToN : int -> string
 
(実行例)
 
> rToN 26;;
val it : string = "Z"
> rToN 27;;
val it : string = "AA"
 
これを利用して
setH2 "E2" [1;2;3]でE2セルから水平方向へ、setV2 "E2" [1;2;3]で垂直方向へ値が書き込まれるように関数setH2とsetV2を定義します。
 
準備としてアルファベットを数値に対応させるmapを作っておきます。
 
> let myMap = 
    [|for c in 'A' .. 'Z' -> c|]
    |> Array.mapi (fun i c -> (c,i))
    |> Map.ofArray;;
 
val myMap : Map<char,int> =
  map
    [('A', 0); ('B', 1); ('C', 2); ('D', 3); ('E', 4); ('F', 5); ('G', 6);
     ('H', 7); ('I', 8); ...]
 
    
 
これを利用して次のように定義します。
 
> let nToRC (ntag : string) =
 
    let rec nToRCSub (rem:list<char>) resR resC   =
        match rem with
        |[] -> 
            (resR,resC)
        |h :: tl when System.Char.IsDigit h ->
            nToRCSub tl (10 * resR + System.Int32.Parse(h.ToString())) resC
        |h :: tl -> 
            let num = myMap.[(System.Char.ToUpper h)]
            nToRCSub tl resR (26*(resC+1) + num)
    
    let (r,c) = (nToRCSub (List.ofArray (ntag.ToCharArray())) 0 -1)
    (r,c+1);;
 
val nToRC : string -> int * int
 
(実行例)
> nToRC "Z2";;
val it : int * int = (2, 26)
 
> nToRC "AA12";;
val it : int * int = (12, 27)
 
小文字でも大丈夫です。
> nToRC "aa12";;
val it : int * int = (12, 27)
 
それではsetH2とsetV2を定義します。
 
> let setH2 (ws:Worksheet) nTag sq =
    let (r,c) = nToRC nTag
    setH ws r c sq
 
let setV2 (ws:Worksheet) nTag sq =
    let (r,c) = nToRC nTag
    setV ws r c sq;;
 
val setH2 : Worksheet -> string -> seq<'a> -> unit
val setV2 : Worksheet -> string -> seq<'a> -> unit
 
 
(実行例)
> setH2 worksheet "C4" ["This";"is";"a";"pen"];;
val it : unit = ()
で 
 
701-1.jpg  
 
となります。
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。