スポンサーサイト

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

F#入門第34回(例外(Exception)(1))

今回のお題は「例外(Exception)(1)」です。
 
まずは宿題の解答から
 
(宿題)
discriminated unionとリストが次のように定義されているとします。
type Fig =
    | Triangle of float*float*float
    | Rectangle of float*float*float*float
let figLst  =[Triangle(3.0,2.0,2.0);Rectangle(1.0,1.0,1.0,2.0);
              Triangle(5.0,6.0,3.0);Rectangle(2.0,1.0,2.0,3.0)]
このとき、for loopを使って、Triangleの3つの要素を
三辺の長さは3.000000と2.000000と2.000000です。
三辺の長さは5.000000と6.000000と3.000000です。
というように表示してください。
 
(解答例)
 
for Triangle(x,y,z) in figLst do
    printfn "三辺の長さは%fと%fと%fです。" x y z
    
 
今日の話題は例外(Exception)です。
.NETでは数を0で割ったり、範囲外の配列のindexにアクセスしたりすると、例外が発生します。この例外は、catchされなければ、すぐさまその関数を脱出し、その関数の呼び出し側の関数へ戻り、そこでもcatchされなければ、さらに、その関数の呼び出し側へもどるというように、呼び出し関数の階層を上って行きます。どこでもcatchされなければ、プログラムはエラーを表示して停止します。
 

 
> 1/0;;
System.DivideByZeroException: Attempted to divide by zero.
   at <StartupCode$FSI_0012>.$FSI_0012.main@()
stopped due to error
 
上の例では、System.DivideByZeroExceptionが発生しましたが、どこでもcatchされなかったので、エラーメッセージと共に停止しました。
 

 
> let a = [|1;2|];;
val a : int array = [|1; 2|]
 
> a.[2];;
System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at <StartupCode$FSI_0014>.$FSI_0014.main@()
stopped due to error
 
さて、プログラム上で、「これが起こったらエラーを表示してプログラムを停止したい」という状況を考えてください。
このような場合F#でよく使われる手法は,関数failwithfを使う方法です。
例をみてください。
 
let checkIndex (a : 'a array) i =
    if i < 0 then 
        failwithf "インデックスが%dで負です。" i
    elif i >= a.Length then 
        failwithf "インデックスが%d配列の長さ%d以上です。" i a.Length
    else  printfn "インデックスは範囲内です。"
 
実行例
 
> checkIndex [|3;4|] 5 ;;
Microsoft.FSharp.Core.FailureException: インデックスが5で配列の長さ2以上です。
   at FSI_0022.checkIndex@358-3.Invoke(String message)
   以下略
 
このように,メッセージ付きのFailureExceptionをRaiseしてくれます。
なを、単にメッセージに数値等を埋め込む必要がないときはfailwith関数を使います。
 
まあ、普通は「これが起こったらエラーを表示してプログラムを停止したい」などという、状況は少ない訳で、「こんなイレギュラーなことが起こったら、どうにか、その状況をさばいて処理していく」という状況がほとんどです。
ということで、まずは.NET側からraiseされた、exceptionを捕まえて、さばく方法を紹介します。
これにはtry with構文を利用します。
まずは、例を見てください。
 
let test () =
    try
        let a = [|1;2|]            //#1
        printfn "%A" a.[5]         //#2
        printfn "yet in try block" //#3
    with
    | ex -> printfn "type = %A  mes = %A " (ex.GetType ()) ex.Message //#4
    
    printfn "now quit test"        //#5
    
tryとwithの間が、例外発生の監視領域で、ここで、例外が発生した場合、その例外は、すぐさまwithの後のパターンマッチに送られます。(この例では#2で例外が発生するので、#3の部分は実行されません。)この例の場合のパターンマッチは、なんにでもマッチして、それを識別子exに束縛します。また.NET側で準備されている例外は、GetType()とMessageというプロパティをもつので、これを利用して、TypeとMessageを表示しています。
withの後のパターンマッチの処理が終われば、元のプログラムの流れに戻り、#5が実行されます。
実行例は次のようになります。
 
> test ();;
type = System.IndexOutOfRangeException  mes = "Index was outside the bounds of the array." 
now quit test
val it : unit = ()
 
このように、.NETから投げかけられる例外はそれぞれのTypeをもちますので、どのtypeと一致するかによるパターンマッチも可能です。
次の例を見てください。
 
let test choice =
    try
        if choice = 0 then
            printfn "%A" [|1;2|].[5]
        else
            printfn "%d" (3/0)
    with
    | :? System.IndexOutOfRangeException    -> printfn "配列境界外"
    | :? System.DivideByZeroException       -> printfn "0で割った"
  
withの|のあとの、:?というのが,Typeとのマッチを調べてくれます。
 
実行例
 
> test 0 ;;
配列境界外
val it : unit = ()
 
> test 1 ;;
0で割った
val it : unit = ()
 
上の例で、型とのマッチを行った時、その例外を何らかの識別子で束縛するということを、行っていませんでした。これは、次のようにasを使うことにより可能になります。
let test choice =
    try
        if choice = 0 then
            printfn "%A" [|1;2|].[5]
        else
            printfn "%d" (3/0)
    with
    | :? System.IndexOutOfRangeException  as ex 
                                -> printfn "mes =%s" ex.Message
    | :? System.DivideByZeroException  as ex  
                                 -> printfn "mes =%s" ex.Message 
 
実行例
 
> test 0 ;;
mes =Index was outside the bounds of the array.
val it : unit = ()
 
> test 1 ;;
mes =Attempted to divide by zero.
val it : unit = ()
 
as idenで、マッチしたもの全体がidenに束縛されます。
普通のパターンマッチでも次のようにasは使えます。
 

 
let matchTest lst =
    match lst with
    | x::y as u -> printfn "hd = %A tl = %A whole=%A" x y u
    | []        -> printfn "empty"  
 
としておいて
 
> matchTest [1;2;3];;
hd = 1 tl = [2; 3] whole=[1; 2; 3]
val it : unit = ()
 
話がasの方にずれましたが、もとにもどします。
 
実はtry with は式で値を返します。よって返る可能性のある値は、すべて同じ型でなければなりません。(先ほどの例ではunit型でした。)
 

 
let test choice =
    try
        if choice = 0 then
            printfn "%A" [|1;2|].[5]
            5
        else
            printfn "%d" (3/0)
            7
    with
    | :? System.IndexOutOfRangeException  
                                ->  1
    | :? System.DivideByZeroException   
                                 -> -1 
 
実行例
 
> test 0;;
val it : int = 1
 
> test 1;;
val it : int = -1
 
さて宿題です。次のように定義して、testTry () と実行すると、どのように表示されるでしょうか?
 
let tryDepth2 () =
    try
        printfn "in Depth2 try first"
        printfn "%d" (3/0)
        printfn "in Depth2 try last"
    with
    | :? System.IndexOutOfRangeException ->printfn "配列境界外"   
    printfn "now quit tryDepth2"
    
let tryDepth1 () =
    try 
        printfn "in Depth1 try first"
        tryDepth2 ()
        printfn "in Depth1 try last"
    with
    | :? System.DivideByZeroException  ->printfn "0で割った"
    printfn "now quit tryDepth1"
 
let testTry () =
    tryDepth1 ()
スポンサーサイト

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

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

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

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

この人とブロともになる

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