文字列の八方向検索
Posted feedbacks - OCaml
OCaml は素の状態だとマルチバイト文字の扱いに難があるので、 テストデータは ASCII なものに差し替えてます。 % ocaml str.cma eight_way_search.ml (2, 0), 左 (0, 1), 右 (0, 1), 下 (3, 1), 右 (4, 3), 左上 かなり冗長なつくりになってますが、ほぼ上から順番に細かくボトムアップで 組み上げていって、最後に出力する部分を書き上げるまで、 ocamlc -i で型を確かめるためのコンパイルしかしていません。 つまり最初に実行した時点で完成しています。 (厳密には表示を例題と合わせるための調整をしたので、その分は実行してます) 少なくとも私は動的型付け言語では、この問題で一発で動くものを書き上げる 自信は無いです。 でも OCaml ではできる。 こういうのが強い静的型付け言語の醍醐味の一つじゃないかと思いますね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | let normalize_string str = Str.global_replace (Str.regexp "^\n") "" str
let array_of_string str =
let len = String.length str in
let ary = Array.make len '\000' in
for i = 0 to len - 1 do
ary.(i) <- str.[i]
done;
ary
let matrix_of_string str =
match Str.split (Str.regexp "\n") (normalize_string str) with
| (x::xs) as lst
when List.for_all (fun s -> String.length x = String.length s) xs
->
Array.map array_of_string (Array.of_list lst)
| _ -> failwith "invalid input"
type direction =
| Upper | Lower | Left | Right
| UpperLeft | UpperRight
| LowerLeft | LowerRight
let tuple_add (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
let rec index_of_direction dir =
let add = tuple_add in
let self = index_of_direction in
match dir with
| Upper -> (-1, 0)
| Lower -> ( 1, 0)
| Left -> (0, -1)
| Right -> (0, 1)
| UpperLeft -> add (self Upper) (self Left)
| UpperRight -> add (self Upper) (self Right)
| LowerLeft -> add (self Lower) (self Left)
| LowerRight -> add (self Lower) (self Right)
let string_of_direction dir =
match dir with
| Upper -> "上"
| Lower -> "下"
| Left -> "左"
| Right -> "右"
| UpperLeft -> "左上"
| UpperRight -> "右上"
| LowerLeft -> "左下"
| LowerRight -> "右下"
let get matrix (x, y) =
try Some matrix.(x).(y)
with Invalid_argument _ -> None
exception Not_exists
let exists matrix ch_ary base_pos dir =
let inc = index_of_direction dir in
let next pos = tuple_add pos inc in
try
let _ =
Array.fold_left begin fun pos ch ->
match get matrix pos with
| Some c when c = ch -> next pos
| _ -> raise Not_exists
end base_pos ch_ary
in
true
with Not_exists -> false
let dirs_map matrix ch_ary base_pos =
let dirs =
[Upper; Lower; Left; Right; UpperLeft; UpperRight; LowerLeft; LowerRight]
in
List.fold_left begin fun acc dir ->
if exists matrix ch_ary base_pos dir
then (base_pos, string_of_direction dir)::acc
else acc
end [] dirs
let search search_base search_str =
let matrix = matrix_of_string search_base in
let ch_ary = array_of_string search_str in
let ch = ch_ary.(0) in
let dmap = dirs_map matrix ch_ary in
let result = ref [] in
let _ =
Array.mapi begin fun x ary ->
Array.mapi begin fun y c ->
if c = ch then
result := List.rev_append (dmap (x, y)) !result
end ary
end matrix
in
!result
let sample_input = "
LOUULU
UOLUOL
OLLOLU
LLOOUO
"
let sample_search_string = "UOL"
let rec print_result result =
match result with
| [] -> print_newline ()
| ((x, y), dir)::rest ->
Printf.printf "(%d, %d), %s\n" y x dir;
print_result rest
let test () =
let result = search sample_input sample_search_string in
print_result (List.rev result)
let () = if not !Sys.interactive then test ()
|
本体より配列に変換するコードがでかいです。 matrix_index "リオウウリウ ウオリウオリ オリリオリウ リリオオウオ" "ウオリ";; といった感じで使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | (*二次元arrayのsub*)
let matrix_sub arr course x y len =
let get (b,a) = arr.(a).(b) in
Array.init len (fun i -> get
(match course with
| `Upper -> (x),(y-i)
| `Lower -> (x),(y+i)
| `Right -> (x+i),(y)
| `Left -> (x-i),(y)
| `UpperRight -> (x+i),(y-i)
| `UpperLeft -> (x-i),(y-i)
| `LowerRight -> (x+i),(y+i)
| `LowerLeft -> (x-i),(y+i)
));;
(*a.(x).(y)から八方向検索。sと同じなら位置と方角をprint*)
let scan8_print a x y s =
List.iter2 (fun course str ->
try
if s = matrix_sub a course x y 3
then Printf.printf "(%d, %d), %s\n" x y str
with _ -> () )
[`Left; `Right; `LowerLeft; `LowerRight;
`Lower; `Upper; `UpperLeft; `UpperRight]
["左"; "右"; "左下"; "右下"; "下"; "上"; "左上"; "右下"];;
(*Array.of_listのrev版*)
let array_of_rev_list = function
| [] -> [||]
| [hd] -> [|hd|]
| hd::x::tl ->
let len = List.length tl + 2 in
let res = Array.make len hd in
let rec loop i = function
| [] -> res
| hd::tl -> (res.(i) <- hd; loop (i-1) tl)
in loop (len-2) (x::tl);;
(*マルチバイト混じりの文字列を,一字ごとに分けたlistに*)
let list_of_mbstr n s =
let len = String.length s in
let rec loop i acc =
if i>=len then acc else
match s.[i] with
| '\000'..'\127' -> loop (i+1) ((String.sub s i 1)::acc)
| _ -> loop (i+n) ((String.sub s i n)::acc)
in loop 0 [];;
(*sをchで分けた二次元arrayにする*)
let matrix_of_mbstr n ch s =
let len = String.length s in
let rec loop i acc =
if i>=len then acc else
let j = try String.index_from s i ch
with Not_found -> len in
loop (j+1) (array_of_rev_list
(list_of_mbstr n (String.sub s i (j-i)))::acc)
in array_of_rev_list (loop 0 []);;
let matrix_for f a =
for i=0 to Array.length a - 1 do
for j=0 to Array.length a.(i) - 1 do f j i done;
done;;
let matrix_index s1 s2 =
let s1 = matrix_of_mbstr 3 '\n' s1 in
let s2 = array_of_rev_list (list_of_mbstr 3 s2) in
matrix_for (fun x y -> scan8_print s1 x y s2) s1;;
|


kuromin #4400() Rating0/2=0.00
[ reply ]