Function内部でHiddenプロパティが効かない件

次のようなコードを書いていた

Function hidenUnusedColumn() As Integer
  Dim rangeStr As String
  rangeStr = 【非表示にする列の範囲を求める関数を実行】
  Columns(rangeStr).Hidden = True
  hidenUnusedColumn = 0
End Function

SubじゃなくてFunctionにしているのは、特に意図はない。


rangeStrは正しく求められている。けどHiddenプロパティが動いてくれない。
数時間悩んだ。
FunctionじゃなくてSubで定義したらちゃんと動いた。


FunctionとSubって、戻り値を返せるかどうかの違いだけだと思ってたけど違うのかな?
Sub内でしか実行できない手続きって何だろう?
逆にFunctionでしか実行できないものってあるのかな。

カレントスキーマの全テーブルの全カラムを一覧するSQL

次の情報も一緒に取得。

  • カラムの型
  • 主キーかどうか
  • NOT NULL制約列かどうか
SELECT
    A.TABLE_NAME
   ,A.COLUMN_NAME
   ,A.COLUMN_ID
   ,A.DATA_TYPE
   ,A.DATA_LENGTH
   ,A.DATA_PRECISION
   ,A.DATA_SCALE
   ,SQ1.CONSTRAINT_TYPE
/*
   ,CASE NVL(SQ1.CONSTRAINT_TYPE,'')
      WHEN 'P' THEN 1
      ELSE 0
    END PKEY
*/
   ,A.NULLABLE
/*
   ,CASE NVL(A.NULLABLE,'')
      WHEN 'N' THEN 1
      ELSE 0
    END NOTNULL
*/
FROM
    USER_TAB_COLUMNS A
   ,(SELECT
        B.TABLE_NAME
       ,B.COLUMN_NAME
       ,C.CONSTRAINT_TYPE
    FROM
        USER_CONS_COLUMNS B
       ,USER_CONSTRAINTS C
    WHERE
        B.OWNER = C.OWNER
    AND B.TABLE_NAME = C.TABLE_NAME
    AND B.CONSTRAINT_NAME = C.CONSTRAINT_NAME
    AND C.CONSTRAINT_TYPE = 'P'
    ) SQ1
WHERE
    A.TABLE_NAME = SQ1.TABLE_NAME(+)
AND A.COLUMN_NAME = SQ1.COLUMN_NAME(+)
ORDER BY
    A.TABLE_NAME
   ,A.COLUMN_ID

図形を拡張メタファイル形式で貼り付ける

Excelで図形(テキストボックス)を多用した書類を作っていざ印刷してみると、印刷結果の図形の大きさが編集の見た目と全然違って困った。
たくさんのテキストボックスを直線でつないで関係性を表すための図だったけど、テキストボックスの大きさが変わってしまい、テキストボックス同士や直線との位置関係が崩れてしまう。
印刷プレビューで見てみると確かにレイアウトが崩れて表示される。編集中の画面はズレてない。
ということで調べてみた。図として貼り付けてやれば編集中の見た目と印刷プレビューが変わることがない、みたいな情報を見つけたのでやってみると、確かにズレなくなった。
しかし、問題が。テキストボックスはたくさん(数百個)ある。手で「形式を選択して貼り付け」なんてやってられない。
そんなこんなで、シート内の図形を、別のシートに拡張メタファイルで貼り付けるExcel VBAを書いたのでメモっとく。

' Book1.xlsm の Sheet1 にある図形を Sheet2 に拡張メタファイル形式で貼り付ける
Sub ShapeCopyEmf()
  Dim myShape As Variant
  Worksheets("Sheet2").Select
  For Each myShape In Workbooks("Book1.xlsm").Worksheets("Sheet1").Shapes
    myShape.Copy
    ActiveSheet.PasteSpecial Format:="図 (拡張メタファイル)"
    Selection.Top = myShape.Top
    Selection.Left = myShape.Left
  Next
End Sub

ループでEval(その1)

evalを再帰で書くとすぐStackOverflowExceptionで死んじゃうので、ループで書いてみようという試み。
単純に走査するだけのコードを前回のエントリで書いたので、それを使いつつ。
こんな感じ。

using System;
using System.Collections.Generic;

abstract class SExp { }

class Pair : SExp
{
  public SExp Car { get; set; }
  public SExp Cdr { get; set; }

  public Pair(SExp car, SExp cdr)
  {
    this.Car = car;
    this.Cdr = cdr;
  }

  public override string ToString()
  {
    return "(" + this.Car + " . " + this.Cdr + ")";
  }
}

class Atom : SExp
{
  public string Value { get; set; }

  public Atom(string str)
  {
    this.Value = str;
  }

  public override string ToString()
  {
    return this.Value;
  }
}

class Nil : SExp 
{
  public override string ToString()
  {
    return "()";
  }
}

class App
{
  static void Main(string[] args)
  {
    // (a b c)
    SExp exp1 = new Pair(new Atom("a"), new Pair(new Atom("b"), new Pair(new Atom("c"), new Nil())));
    // (a b c . d)
    SExp exp2 = new Pair(new Atom("a"), new Pair(new Atom("b"), new Pair(new Atom("c"), new Atom("d"))));
    // (x y (a b c) z)
    SExp exp3 = new Pair(new Atom("x"), new Pair(new Atom("y"), new Pair(exp1, new Pair(new Atom("z"), new Nil()))));

    SExp result = Scan(exp1);
    Console.WriteLine("result: " + result);
    Console.WriteLine("==========");

    result = Scan(exp2);
    Console.WriteLine("result: " + result);
    Console.WriteLine("==========");

    result = Scan(exp3);
    Console.WriteLine("result: " + result);
    Console.WriteLine("==========");

    result = Scan(new Atom("hoge"));
    Console.WriteLine("result: " + result);
    Console.WriteLine("==========");

    result = Scan(new Nil());
    Console.WriteLine("result: " + result);
    Console.WriteLine("==========");
  }

  static SExp Scan(SExp exp)
  {
    Stack<SExp> stk1 = new Stack<SExp>();
    Stack<Frame> stk2 = new Stack<Frame>();
    Frame frame = new Frame();
    int sqn = 0;

    SExp result = null;

    while (true)
    {
      if (exp is Pair)
      {
        SExp car = (exp as Pair).Car;
        SExp cdr = (exp as Pair).Cdr;

        if (cdr is Nil || cdr is Atom)
        {
          frame.State = Frame.StateForEval.LastCellScaned;
        }

        stk1.Push(cdr);
        exp = car;

        if (car is Pair)
        {
          Console.WriteLine("push");
          stk2.Push(frame);
          frame = new Frame();
        }
      }
      else
      {
        Console.WriteLine("eval: " + exp);
        result = exp;
        if (frame.State == Frame.StateForEval.LastCarEvaled)
        {
          frame.State = Frame.StateForEval.CanEval;
        }
        if (frame.State == Frame.StateForEval.LastCellScaned)
        {
          frame.State = Frame.StateForEval.LastCarEvaled;
        }

        if (frame.State == Frame.StateForEval.CanEval)
        {
          if (frame.List is Nil)
          {
            frame.List = new Pair(exp, new Nil());
          }
          else
          {
            Pair last = GetLastPair(frame.List as Pair);
            if (exp is Atom)
            {
              last.Cdr = exp;
            }
          }

          // 評価した部分リストは、表示短縮のために E1, E2, E3,... で表記しておく
          sqn++;
          Console.WriteLine("EVAL: " + frame.List + " => E" + sqn);
          Atom a = new Atom("E" + sqn);

          if (stk2.Count == 0)
          {
            result = a;
          }
          else
          {
            Console.WriteLine("pop");
            frame = stk2.Pop();

            if (frame.State == Frame.StateForEval.LastCarEvaled)
            {
              frame.State = Frame.StateForEval.CanEval;
            }
            if (frame.State == Frame.StateForEval.LastCellScaned)
            {
              frame.State = Frame.StateForEval.LastCarEvaled;
            }

            if (frame.List is Nil)
            {
              frame.List = new Pair(exp, new Nil());
            }
            else
            {
              Pair last = GetLastPair(frame.List as Pair);
              last.Cdr = new Pair(a, new Nil());
            }
          }
        }
        else
        {
          if (frame.List is Nil)
          {
            frame.List = new Pair(exp, new Nil());
          }
          else
          {
            Pair last = GetLastPair(frame.List as Pair);
            last.Cdr = new Pair(exp, new Nil());
          }
        }

        if (0 == stk1.Count) break;
        exp = stk1.Pop();
      }
    }

    return result;
  }

  class Frame
  {
    public SExp List { get; set; }
    public StateForEval State { get; set; }

    public Frame()
    {
      List = new Nil();
      State = StateForEval.None;
    }

    public enum StateForEval
    {
      // まだ最後のコンスセルまで来てない
      None,
      // 最後のコンスセル読み込んだ
      LastCellScaned,
      // 最後のコンスセルのcarを処理した
      LastCarEvaled,
      // 最後のコンスセルのcdrを処理した(リストの要素全部を評価した)
      CanEval,
    }
  }

  // 最後のノード取得は番兵でやるべきだろ
  static Pair GetLastPair(Pair list)
  {
    while (list.Cdr is Pair)
    {
      list = list.Cdr as Pair;
    }
    return list;
  }
}

実行結果→Ideone.com - sh0FXd - Online C# Compiler & Debugging Tool
スタック2個使ったけど、大丈夫なのかな。変な事してないかな...。

  • 構文木を走査するためのスタック(stk1)
  • リストの評価がどこまで終わってるか覚えておくためのスタック(stk2)

今回は手続きだけを想定して書いたけど、構文(defineやlambdaなど)は、それ独自の評価ルールがあるから、どげんかせんといかん。

S式の要素を走査する

再帰とループの2通りの方法でS式(リスト)を走査してみる。ループの方はスタックの使い方これでいいのかよくわからんけど。

using System;
using System.Collections.Generic;

abstract class SExp { }

class Pair : SExp
{
  public SExp Car { get; private set; }
  public SExp Cdr { get; private set; }

  public Pair(SExp car, SExp cdr)
  {
    this.Car = car;
    this.Cdr = cdr;
  }

  public override string ToString()
  {
    return "(" + this.Car + " . " + this.Cdr + ")";
  }
}

class Atom : SExp
{
  public string Value { get; set; }

  public Atom(string str)
  {
    this.Value = str;
  }

  public override string ToString()
  {
    return this.Value;
  }
}

class Nil : SExp 
{
  public override string ToString()
  {
    return "()";
  }
}

class App
{
  static void Main(string[] args)
  {
    // exp2 == (x y (a b c) z)
    SExp exp1 = new Pair(new Atom("a"), new Pair(new Atom("b"), new Pair(new Atom("c"), new Nil())));
    SExp exp2 = new Pair(new Atom("x"), new Pair(new Atom("y"), new Pair(exp1, new Pair(new Atom("z"), new Nil()))));

    Scan1(exp2);
    Console.WriteLine("==========");
    Scan2(exp2);
  }

  // 再帰処理で走査
  static void Scan1(SExp exp)
  {
    if (exp is Pair)
    {
      Scan1((exp as Pair).Car);
      Scan1((exp as Pair).Cdr);
    }
    else
    {
      Console.WriteLine(exp);
    }
  }

  // ループ+スタックで走査
  static void Scan2(SExp exp)
  {
    Stack<SExp> cs = new Stack<SExp>();
    while (true)
    {
      if (exp is Pair)
      {
        SExp car = (exp as Pair).Car;
        SExp cdr = (exp as Pair).Cdr;
        cs.Push(cdr);
        exp = car;
      }
      else
      {
        Console.WriteLine(exp);
        if (0 == cs.Count) break;
        exp = cs.Pop();
      }
    }
  }
}

car部にPairがくると木構造(?)になってしまう。car部の部分木を走査し終わった後にcdr部の部分木を走査するためには、位置を記憶しておかないといけないので、スタックが必要になる。メソッド呼び出しは、この「戻ってくる場所をスタックで記憶しておく」という仕事を処理系が裏でやってくれるので簡潔に書ける。

以前二分木のイテレータで散々悩んだときのやり方でも考えてみるかな。S式も木構造だけど、あの時の木構造と違うのは、ノードが「自分の値」を持っていない(leftとrightだけ)ってことなのかな?

階乗計算

言語が提供するコールスタックを利用せずに再帰の要領で計算したくなった。特にオチは無い。

using System;
using System.Collections.Generic;

class App
{
  static public void Main(string[] args)
  {
    const int N = 6;

    /////////////////////////////////////
    // 6!=720 を、3つの方法でやってみた。
    /////////////////////////////////////

    // 1. 素朴にループで
    Console.WriteLine(Fact1(N));

    // 2. 素朴に再帰で
    Console.WriteLine(Fact2(N));

    // 3. コールスタックを自前で用意して再帰っぽく
    Console.WriteLine(Fact3(N));
  }

  // 素朴にループ
  static int Fact1(int n)
  {
    int prod = 1;
    for (int i = 0; i < n; i++)
    {
      prod *= (i + 1);
    }
    return prod;
  }

  // 素朴に再帰
  static int Fact2(int n)
  {
    if (n == 0) return 1;
    return n * Fact2(n - 1);
  }

  // コールスタックに積まれる情報
  class Frame
  {
    public int Arg { get; set; }
    public int Ret { get; set; }
    public Frame(int arg) { Arg = arg; }
  }

  // C#が提供するコールスタックを使わず
  // スタックを自前で用意
  static int Fact3(int n)
  {
    // なんちゃってコールスタック
    Stack<Frame> frames = new Stack<Frame>();

    Frame f = new Frame(n);
    while(true)
    {
      if (f.Arg == 0)
      {
        // 再帰の出口
        f.Ret = 1;
        break;
      }
      else
      {
        // 再帰呼び出し
        frames.Push(f);
        f = new Frame(f.Arg - 1);
      }
    }

    // 残りの掛け算
    while (0 < frames.Count)
    {
      Frame peek = frames.Peek();
      peek.Ret = f.Ret * peek.Arg;
      f = frames.Pop();
    }
    return f.Ret;
  }
}

動かしてみた。ちゃんと動いてるっぽい。

一度に複数レコードをINSERT

仕事でOracle触ってる。テストデータとして大量のレコードをINSERTしたかったので、

INSERT ALL
INTO TABLE1 (FIELD1, FIELD2, FIELD3, ...) VALUES (VALUE11, VALUE12, VALUE13, ...)
INTO TABLE1 (FIELD1, FIELD2, FIELD3, ...) VALUES (VALUE21, VALUE22, VALUE23, ...)
INTO TABLE1 (FIELD1, FIELD2, FIELD3, ...) VALUES (VALUE31, VALUE32, VALUE33, ...)
...
SELECT * FROM DUAL;

ってやってみたけど、レコードが多くなった場合(カラム数が数十、レコード数が数千件)にORA-24335のエラーが出た。次のようにしたら大丈夫だった。

INSERT INTO TABLE1 (FIELD1, FIELD2, FIELD3, ...) (
    SELECT VALUE11, VALUE12, VALUE13, ... FROM DUAL UNION ALL
    SELECT VALUE21, VALUE22, VALUE23, ... FROM DUAL UNION ALL
    SELECT VALUE31, VALUE32, VALUE33, ... FROM DUAL UNION ALL
    ...
)

解決方法として妥当なのか良くわからんけど、まぁいいや。