Advertisement
Guest User

Untitled

a guest
Nov 25th, 2015
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.18 KB | None | 0 0
  1.  
  2.  
  3. # RLE圧縮をトップダウンで実装する(Java)
  4.  
  5. 「関数プログラミング実践入門」6章の例題をJava実装する
  6.  
  7.  
  8. ## らしからぬ Java 実装
  9.  
  10. RLE.compress("AAAABBBCC") とすると、戻り値 "A4B3C2" を返却するというテストを用意
  11.  
  12. ```Java
  13. //テスト
  14. @Test
  15. public void test() throws Exception {
  16. //期待する振る舞い
  17. assertThat(RLE.compress("AAAABBBCC"), is("A4B3C2"));
  18. }
  19.  
  20. //RLE実装
  21. public static class RLE {
  22. //エントリポイント
  23. public static String compress(final String str) {
  24. return null;
  25. }
  26. }
  27. ```
  28.  
  29. compress を toCharAndRunLength関数と fromCharAndRunLength関数に分割するトップダウン・アプローチ
  30.  
  31. ```Java
  32. public static class RLE {
  33. //エントリポイント
  34. public static String compress(final String str) {
  35. //関数合成
  36. final Function<String, String> executor =
  37. toCharAndRunLength.andThen(fromCharAndRunLength);
  38. return executor.apply(str);
  39. }
  40.  
  41. // "AAAABBBCC" -> [("A",4),("B",3),("C",2)] する関数
  42. private static final Function<String, List<Tuple<String, Integer>>> toCharAndRunLength =
  43. str -> null;
  44.  
  45. // [("A",4),("B",3),("C",2)] -> "A4B3C2" する関数
  46. private static final Function<List<Tuple<String, Integer>>, String> fromCharAndRunLength =
  47. list -> null;
  48. }
  49. ```
  50.  
  51. ## fromCharAndRunLength関数の実装
  52.  
  53. fromCharAndRunLength関数 を rls2strs関数と cat関数に更に分割する
  54.  
  55. ```Java
  56. // [("A",4),("B",3),("C",2)] -> ["A4","B3","C2"] する関数
  57. private static final Function<List<Tuple<String, Integer>>, List<String>> rls2strs =
  58. list -> null;
  59.  
  60. // ["A4","B3","C2"] -> "A4B3C2" する関数
  61. private static final Function<List<String>, String> cat =
  62. list -> null;
  63.  
  64. // [("A",4),("B",3),("C",2)] -> "A4B3C2" する関数
  65. private static final Function<List<Tuple<String, Integer>>, String> fromCharAndRunLength =
  66. list -> rls2strs.andThen(cat).apply(list);
  67. ```
  68.  
  69. cat関数を実装する
  70.  
  71. ```Java
  72. // ["A4","B3","C2"] -> "A4B3C2" する関数
  73. private static final Function<List<String>, String> cat =
  74. list -> list.stream()
  75. .collect(Collectors.joining());
  76. ```
  77.  
  78. rls2strs関数を実装する
  79.  
  80. ```Java
  81. // [("A",4),("B",3),("C",2)] -> ["A4","B3","C2"] する関数
  82. private static final Function<List<Tuple<String, Integer>>, List<String>> rls2strs =
  83. list -> list.stream()
  84. .map(tuple -> String.format("%s%s",
  85. tuple._1,
  86. tuple._2.toString()))
  87. .collect(Collectors.toList());
  88. ```
  89.  
  90.  
  91. ## toCharAndRunLength関数の実装
  92.  
  93. toCharAndRunLength関数を toRLs関数と toPairs関数に分割する
  94.  
  95. ```Java
  96. // "AAAABBBCC" -> ["AAAA","BBB","CC"] する関数
  97. private static final Function<String, List<String>> toRLs =
  98. str -> null;
  99.  
  100. // ["AAAA","BBB","CC"] -> [("A",4),("B",3),("C",2)] する関数
  101. private static final Function<List<String>, List<Tuple<String, Integer>>> toPairs =
  102. list -> null;
  103.  
  104. // "AAAABBBCC" -> [("A",4),("B",3),("C",2)] する関数
  105. private static final Function<String, List<Tuple<String, Integer>>> toCharAndRunLength =
  106. str -> toRLs.andThen(toPairs).apply(str);
  107. ```
  108.  
  109. toPairs関数を実装する
  110.  
  111. ```Java
  112. // ["AAAA","BBB","CC"] -> [("A",4),("B",3),("C",2)] する関数
  113. private static final Function<List<String>, List<Tuple<String, Integer>>> toPairs =
  114. list -> list.stream()
  115. .map(str -> new Tuple<String, Integer>(str.substring(0, 1), str.length()))
  116. .collect(Collectors.toList());
  117. ```
  118.  
  119. toRLs関数を実装する。Haskell であれば、標準ライブラリの group 関数で済むところが。。
  120.  
  121. ```Java
  122. // "AAAABBBCC" -> ["AAAA","BBB","CC"] する関数
  123. private static final Function<String, List<String>> toRLs = str -> {
  124. //同じ文字が出現する間、この関数外変数に値を保持する
  125. final String[] stack = {""};
  126. final Function<String, List<String>> executor = strings -> {
  127. //文字列を一文字ずつに分割する
  128. final String[] chars = strings.split("");
  129. final List<String> collect = Stream.of(chars).map(chr -> {
  130. //スタックが空なら
  131. if (isEmpty(stack[0])) {
  132. stack[0] = chr;
  133. return null;
  134. }
  135. //スタック保持文字と同じなら
  136. if (stack[0].startsWith(chr)) {
  137. stack[0] = stack[0] + chr;
  138. return null;
  139. }
  140. //スタック保持文字と異なるなら
  141. final String result = Stream.of(stack).collect(Collectors.joining());
  142. stack[0] = chr;
  143. return result;
  144. }).collect(Collectors.toList());
  145. //スタック保持文字列を追加
  146. collect.add(stack[0]);
  147. //Null除外して返却
  148. return collect.stream()
  149. .filter(Objects::nonNull)
  150. .collect(Collectors.toList());
  151. };
  152. return executor.apply(str);
  153. };
  154. ```
  155.  
  156. ## インラインに展開
  157.  
  158. すべて実装したので、toCharAndRunLength関数と fromCharAndRunLength関数をインライン化する
  159.  
  160. ```Java
  161. public static String compress(final String str) {
  162. final Function<String, String> executor = toRLs.andThen(toPairs)
  163. .andThen(rls2strs)
  164. .andThen(cat);
  165. return executor.apply(str);
  166. }
  167. ```
  168.  
  169. テストが正しく動作することを確認する
  170.  
  171. ```Java
  172. //テスト
  173. @Test
  174. public void test() throws Exception {
  175. //期待する振る舞い
  176. assertThat(RLE.compress("AAAABBBCC"), is("A4B3C2"));
  177. assertThat(RLE.compress("AABCCCDDD"), is("A2B1C3D3"));
  178. }
  179.  
  180. //RLE実装
  181. public static class RLE {
  182. //エントリポイント
  183. public static String compress(final String str) {
  184. final Function<String, String> executor = toRLs.andThen(toPairs)
  185. .andThen(rls2strs)
  186. .andThen(cat);
  187. return executor.apply(str);
  188. }
  189.  
  190. // "AAAABBBCC" -> ["AAAA","BBB","CC"] する関数
  191. private static final Function<String, List<String>> toRLs = str -> {
  192. final String[] stack = {""};
  193. final Function<String, List<String>> executor = strings -> {
  194. //文字列を一文字ずつに分割する
  195. final String[] chars = strings.split("");
  196. final List<String> collect = Stream.of(chars).map(chr -> {
  197. //スタックが空なら
  198. if (isEmpty(stack[0])) {
  199. stack[0] = chr;
  200. return null;
  201. }
  202. //スタック保持文字と同じなら
  203. if (stack[0].startsWith(chr)) {
  204. stack[0] = stack[0] + chr;
  205. return null;
  206. }
  207. //スタック保持文字と異なるなら
  208. final String result = Stream.of(stack).collect(Collectors.joining());
  209. stack[0] = chr;
  210. return result;
  211. }).collect(Collectors.toList());
  212. //スタック保持文字列を追加
  213. collect.add(stack[0]);
  214. //Null除外
  215. return collect.stream().filter(Objects::nonNull).collect(Collectors.toList());
  216. };
  217. return executor.apply(str);
  218. };
  219.  
  220. // ["AAAA","BBB","CC"] -> [("A",4),("B",3),("C",2)] する関数
  221. private static final Function<List<String>, List<Tuple<String, Integer>>> toPairs =
  222. list -> list.stream()
  223. .map(str -> new Tuple<String, Integer>(str.substring(0, 1), str.length()))
  224. .collect(Collectors.toList());
  225.  
  226. // [("A",4),("B",3),("C",2)] -> ["A4","B3","C2"] する関数
  227. private static final Function<List<Tuple<String, Integer>>, List<String>> rls2strs =
  228. list -> list.stream()
  229. .map(tuple -> String.format("%s%s", tuple._1, tuple._2.toString()))
  230. .collect(Collectors.toList());
  231.  
  232. // ["A4","B3","C2"] -> "A4B3C2" する関数
  233. private static final Function<List<String>, String> cat =
  234. list -> list.stream()
  235. .collect(Collectors.joining());
  236. }
  237. ```
  238.  
  239. ## その他
  240.  
  241. Java8の場合、stream() と collect() を書くのが手間なので、Streamで渡した方がコード量が削減できる。
  242.  
  243. ```Java
  244. // Stream["AAAA","BBB","CC"] -> Stream[("A",4),("B",3),("C",2)] する関数
  245. private static final Function<Stream<String>, Stream<Tuple<String, Integer>>> toPairs =
  246. stream -> stream.map(str -> new Tuple<String, Integer>(str.substring(0, 1), str.length()));
  247.  
  248. // Stream[("A",4),("B",3),("C",2)] -> Stream["A4","B3","C2"] する関数
  249. private static final Function<Stream<Tuple<String, Integer>>, Stream<String>> rls2strs =
  250. stream -> stream.map(tuple -> String.format("%s%s", tuple._1, tuple._2.toString()));
  251.  
  252. // Stream["A4","B3","C2"] -> "A4B3C2" する関数
  253. private static final Function<Stream<String>, String> cat =
  254. stream -> stream.collect(Collectors.joining());
  255. ```
  256.  
  257. またすべて関数合成してから apply() するよりも、早めにstream化した方が、map() や collect() をインラインで記述できるため、Java8的には書きやすい。
  258.  
  259. ```Java
  260. public static class RLE {
  261. //エントリポイント
  262. public static String compress(final String str) {
  263. return toRLs.apply(str)
  264. .map(toPairs)
  265. .map(rls2strs)
  266. .collect(Collectors.joining());
  267. }
  268.  
  269. // "AAAABBBCC" -> Stream["AAAA","BBB","CC"] する関数
  270. private static final Function<String, Stream<String>> toRLs = str -> {
  271. //省略
  272. return executor.apply(str).stream();
  273. };
  274.  
  275. // ["AAAA","BBB","CC"] -> [("A",4),("B",3),("C",2)] する関数
  276. private static final Function<String, Tuple<String, Integer>> toPairs =
  277. str -> new Tuple<String, Integer>(str.substring(0, 1), str.length());
  278.  
  279. // [("A",4),("B",3),("C",2)] -> ["A4","B3","C2"] する関数
  280. private static final Function<Tuple<String, Integer>, String> rls2strs =
  281. tuple -> String.format("%s%s", tuple._1, tuple._2.toString());
  282. }
  283. ```
  284.  
  285. mapは複数回処理せずに、合成した関数を一度で処理する
  286.  
  287. ```Java
  288. public static class RLE {
  289. return toRLs.apply(str)
  290. .map(toPairs.andThen(rls2strs))
  291. .collect(Collectors.joining());
  292. }
  293. ```
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement