1 // Scala Lecture 2 |
1 // Scala Lecture 2 |
2 //================= |
2 //================= |
3 |
3 |
4 |
4 |
|
5 // Overloaded math operations |
|
6 |
|
7 (100 / 4) |
|
8 |
|
9 (100 / 3) |
|
10 |
|
11 (100.toDouble / 3.toDouble) |
|
12 |
|
13 |
|
14 // For-Comprehensions again |
|
15 //========================== |
|
16 |
|
17 def square(n: Int) : Int = n * n |
|
18 |
|
19 for (n <- (1 to 10).toList) yield { |
|
20 val res = square(n) |
|
21 res |
|
22 } |
|
23 |
|
24 // like in functions, the "last" item inside the yield |
|
25 // will be returned; the last item is not necessarily |
|
26 // the last line |
|
27 |
|
28 for (n <- (1 to 10).toList) yield { |
|
29 if (n % 2 == 0) n |
|
30 else square(n) |
|
31 } |
|
32 |
|
33 |
|
34 // ...please, please do not write: |
|
35 val lst = List(1, 2, 3, 4, 5, 6, 7, 8, 9) |
|
36 |
|
37 for (i <- (0 until lst.length).toList) yield square(lst(i)) |
|
38 |
|
39 // this is just so prone to off-by-one errors; |
|
40 // write instead |
|
41 |
|
42 for (e <- lst) yield square(e) |
|
43 |
|
44 |
|
45 //this works for sets as well |
|
46 val st = Set(1, 2, 3, 4, 5, 6, 7, 8, 9) |
|
47 |
|
48 for (e <- st) yield { |
|
49 if (e < 5) e else square(e) |
|
50 } |
|
51 |
|
52 |
|
53 |
|
54 // Side-Effects |
|
55 //============== |
|
56 |
|
57 // with only a side-effect (no list is produced), |
|
58 // has no "yield" |
|
59 |
|
60 for (n <- (1 to 10)) println(n) |
|
61 |
|
62 |
|
63 for (n <- (1 to 10)) { |
|
64 print("The number is: ") |
|
65 print(n) |
|
66 print("\n") |
|
67 } |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 // know when to use yield and when not: |
|
73 |
|
74 for (e <- Set(1, 2, 3, 4, 5, 6, 7, 8, 9); if e < 5) yield square(e) |
|
75 |
|
76 |
5 // Option type |
77 // Option type |
6 //============= |
78 //============= |
7 |
79 |
8 //in Java if something unusually happens, you return null; |
80 //in Java, if something unusually happens, you return null; |
9 //in Scala you use Option |
81 //in Scala you use Option |
10 // - if the value is present, you use Some(value) |
82 // - if the value is present, you use Some(value) |
11 // - if no value is present, you use None |
83 // - if no value is present, you use None |
12 |
84 |
13 |
85 |
14 List(7,2,3,4,5,6).find(_ < 4) |
86 List(7,2,3,4,5,6).find(_ < 4) |
15 List(5,6,7,8,9).find(_ < 4) |
87 List(5,6,7,8,9).find(_ < 4) |
16 |
88 |
17 |
89 |
18 // Values in types |
90 // some operations on Option's |
19 // |
|
20 // Boolean: |
|
21 // Int: |
|
22 // String: |
|
23 // |
|
24 // Option[String]: |
|
25 // |
|
26 |
|
27 |
91 |
28 val lst = List(None, Some(1), Some(2), None, Some(3)) |
92 val lst = List(None, Some(1), Some(2), None, Some(3)) |
29 |
93 |
30 lst.flatten |
94 lst.flatten |
31 |
95 |
38 |
102 |
39 for ((x, y) <- ps) yield { |
103 for ((x, y) <- ps) yield { |
40 if (y == 0) None else Some(x / y) |
104 if (y == 0) None else Some(x / y) |
41 } |
105 } |
42 |
106 |
43 // getOrElse is for setting a default value |
107 // use .getOrElse is for setting a default value |
44 |
108 |
45 val lst = List(None, Some(1), Some(2), None, Some(3)) |
109 val lst = List(None, Some(1), Some(2), None, Some(3)) |
|
110 |
46 for (x <- lst) yield x.getOrElse(0) |
111 for (x <- lst) yield x.getOrElse(0) |
47 |
112 |
48 |
113 |
49 |
114 |
50 |
115 |
51 // error handling with Option (no exceptions) |
116 // error handling with Options (no exceptions) |
|
117 // |
|
118 // Try(....) |
52 // |
119 // |
53 // Try(something).getOrElse(what_to_do_in_an_exception) |
120 // Try(something).getOrElse(what_to_do_in_an_exception) |
54 // |
121 // |
55 import scala.util._ |
122 import scala.util._ |
|
123 |
|
124 Try(1 + 3) |
|
125 Try(9 / 0) |
|
126 |
|
127 Try(9 / 3).getOrElse(42) |
|
128 Try(9 / 0).getOrElse(42) |
|
129 |
|
130 |
56 import io.Source |
131 import io.Source |
57 |
132 |
58 Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString |
133 val my_url = """https://nms.kcl.ac.uk/christian.urban""" |
59 |
134 |
60 Try(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString).getOrElse("") |
135 Source.fromURL(my_url).mkString |
61 |
136 |
62 Try(Some(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString)).getOrElse(None) |
137 Try(Source.fromURL(my_url).mkString).getOrElse("") |
|
138 |
|
139 Try(Some(Source.fromURL(my_url).mkString)).getOrElse(None) |
|
140 |
63 |
141 |
64 // a function that turns strings into numbers |
142 // a function that turns strings into numbers |
65 Integer.parseInt("12u34") |
143 Integer.parseInt("1234") |
|
144 |
66 |
145 |
67 def get_me_an_int(s: String): Option[Int] = |
146 def get_me_an_int(s: String): Option[Int] = |
68 Try(Some(Integer.parseInt(s))).getOrElse(None) |
147 Try(Some(Integer.parseInt(s))).getOrElse(None) |
69 |
148 |
70 val lst = List("12345", "foo", "5432", "bar", "x21") |
149 val lst = List("12345", "foo", "5432", "bar", "x21") |
|
150 |
71 for (x <- lst) yield get_me_an_int(x) |
151 for (x <- lst) yield get_me_an_int(x) |
72 |
152 |
73 // summing all the numbers |
153 // summing all the numbers |
74 val sum = lst.flatMap(get_me_an_int(_)).sum |
154 val sum = (for (i <- lst) yield get_me_an_int(i)).flatten.sum |
75 |
155 |
76 |
156 |
77 // This may not look any better than working with null in Java, but to |
157 // This may not look any better than working with null in Java, but to |
78 // see the value, you have to put yourself in the shoes of the |
158 // see the value, you have to put yourself in the shoes of the |
79 // consumer of the get_me_an_int function, and imagine you didn't |
159 // consumer of the get_me_an_int function, and imagine you didn't |
80 // write that function. |
160 // write that function. |
81 // |
161 // |
82 // In Java, if you didn't write this function, you'd have to depend on |
162 // In Java, if you didn't write this function, you'd have to depend on |
83 // the Javadoc of the get_me_an_int. If you didn't look at the Javadoc, |
163 // the Javadoc of get_me_an_int. If you didn't look at the Javadoc, |
84 // you might not know that get_me_an_int could return a null, and your |
164 // you might not know that get_me_an_int could return a null, and your |
85 // code could potentially throw a NullPointerException. |
165 // code could potentially throw a NullPointerException. |
86 |
166 |
87 |
167 |
88 |
|
89 // even Scala is not immune to problems like this: |
168 // even Scala is not immune to problems like this: |
90 |
169 |
91 List(5,6,7,8,9).indexOf(7) |
170 List(5,6,7,8,9).indexOf(7) |
92 |
171 |
|
172 |
|
173 |
|
174 |
|
175 // Higher-Order Functions |
|
176 //======================== |
|
177 |
|
178 // functions can take functions as arguments |
|
179 |
|
180 val lst = (1 to 10).toList |
|
181 |
|
182 def even(x: Int) : Boolean = x % 2 == 0 |
|
183 def odd(x: Int) : Boolean = x % 2 == 1 |
|
184 |
|
185 lst.filter(x => even(x)) |
|
186 lst.filter(even(_)) |
|
187 lst.filter(even) |
|
188 |
|
189 lst.find(_ > 8) |
|
190 |
|
191 // map applies a function to each element of a list |
|
192 |
|
193 def square(x: Int): Int = x * x |
|
194 |
|
195 lst.map(square) |
|
196 |
|
197 lst.map(square).filter(_ > 4) |
|
198 |
|
199 lst.map(square).filter(_ > 4).map(square) |
|
200 |
|
201 // map works for most collection types, including sets |
|
202 Set(1, 3, 6).map(square) |
|
203 |
|
204 |
|
205 // Why could functions as arguments be useful? |
|
206 // |
|
207 // Consider the sum between a and b: |
|
208 |
|
209 def sumInts(a: Int, b: Int) : Int = |
|
210 if (a > b) 0 else a + sumInts(a + 1, b) |
|
211 |
|
212 |
|
213 sumInt(10, 16) |
|
214 |
|
215 // sum squares |
|
216 def square(n: Int) : Int = n * n |
|
217 |
|
218 def sumSquares(a: Int, b: Int) : Int = |
|
219 if (a > b) 0 else square(a) + sumSquares(a + 1, b) |
|
220 |
|
221 sumSquares(2, 6) |
|
222 |
|
223 |
|
224 // sum factorials |
|
225 def fact(n: Int) : Int = |
|
226 if (n == 0) 1 else n * fact(n - 1) |
|
227 |
|
228 def sumFacts(a: Int, b: Int) : Int = |
|
229 if (a > b) 0 else fact(a) + sumFacts(a + 1, b) |
|
230 |
|
231 sumFacts(2, 6) |
|
232 |
|
233 |
|
234 |
|
235 // You can see the pattern....can we simplify out work? |
|
236 // The type of functions from ints to ints: Int => Int |
|
237 |
|
238 def sum(f: Int => Int, a: Int, b: Int) : Int = { |
|
239 if (a > b) 0 |
|
240 else f(a) + sum(f, a + 1, b) |
|
241 } |
|
242 |
|
243 |
|
244 def sumSquares(a: Int, b: Int) : Int = sum(square, a, b) |
|
245 def sumFacts(a: Int, b: Int) : Int = sum(fact, a, b) |
|
246 |
|
247 // What should we do for sumInts? |
|
248 |
|
249 def id(n: Int) : Int = n |
|
250 def sumInts(a: Int, b: Int) : Int = sum(id, a, b) |
|
251 |
|
252 |
|
253 |
|
254 // Anonymous Functions: You can also write: |
|
255 |
|
256 def sumCubes(a: Int, b: Int) : Int = sum(x => x * x * x, a, b) |
|
257 def sumSquares(a: Int, b: Int) : Int = sum(x => x * x, a, b) |
|
258 def sumInts(a: Int, b: Int) : Int = sum(x => x, a, b) |
|
259 |
|
260 |
|
261 // other function types |
|
262 // |
|
263 // f1: (Int, Int) => Int |
|
264 // f2: List[String] => Option[Int] |
|
265 // ... |
|
266 |
|
267 |
|
268 // Function Composition |
|
269 //====================== |
|
270 |
|
271 // How could Higher-Order Functions and Options be helpful? |
|
272 |
|
273 def add_footer(msg: String) : String = msg ++ " - Sent from iOS" |
|
274 |
|
275 def valid_msg(msg: String) : Boolean = msg.size <= 140 |
|
276 |
|
277 def duplicate(s: String) : String = s ++ s |
|
278 |
|
279 // they compose nicely |
|
280 valid_msg(add_footer("Hello World")) |
|
281 valid_msg(duplicate(add_footer("Hello World"))) |
|
282 |
|
283 |
|
284 // first_word: let's first do it the ugly Java way using null: |
|
285 |
|
286 def first_word(msg: String) : String = { |
|
287 val words = msg.split(" ") |
|
288 if (words(0) != "") words(0) else null |
|
289 } |
|
290 |
|
291 duplicate(first_word("Hello World")) |
|
292 duplicate(first_word("")) |
|
293 |
|
294 def extended_duplicate(s: String) : String = |
|
295 if (s != null) s ++ s else null |
|
296 |
|
297 extended_duplicate(first_word("")) |
|
298 |
|
299 |
|
300 // Avoid always null! |
|
301 def better_first_word(msg: String) : Option[String] = { |
|
302 val words = msg.split(" ") |
|
303 if (words(0) != "") Some(words(0)) else None |
|
304 } |
|
305 |
|
306 better_first_word("Hello World").map(duplicate) |
|
307 better_first_word("Hello World").map(duplicate).map(duplicate).map(valid_msg) |
|
308 |
|
309 better_first_word("").map(duplicate) |
|
310 better_first_word("").map(duplicate).map(valid_msg) |
93 |
311 |
94 |
312 |
95 |
313 |
96 |
314 |
97 // Type abbreviations |
315 // Type abbreviations |
98 //==================== |
316 //==================== |
99 |
317 |
100 // some syntactic convenience |
318 // some syntactic convenience |
|
319 |
101 type Pos = (int, Int) |
320 type Pos = (int, Int) |
102 |
|
103 type Board = List[List[Int]] |
321 type Board = List[List[Int]] |
104 |
322 |
105 |
323 |
106 |
324 |
107 // Implicits |
325 // Implicits (Cool Feature) |
108 //=========== |
326 //========================= |
109 // |
327 // |
110 // for example adding your own methods to Strings: |
328 // For example adding your own methods to Strings: |
111 // imagine you want to increment strings, like |
329 // Imagine you want to increment strings, like |
112 // |
330 // |
113 // "HAL".increment |
331 // "HAL".increment |
114 // |
332 // |
115 // you can avoid ugly fudges, like a MyString, by |
333 // you can avoid ugly fudges, like a MyString, by |
116 // using implicit conversions |
334 // using implicit conversions. |
117 |
335 |
118 |
336 |
119 implicit class MyString(s: String) { |
337 implicit class MyString(s: String) { |
120 def increment = for (c <- s) yield (c + 1).toChar |
338 def increment = for (c <- s) yield (c + 1).toChar |
121 } |
339 } |
122 |
340 |
123 "HAL".increment |
341 "HAL".increment |
124 |
342 |
125 |
343 |
126 // No return in Scala |
344 |
|
345 // No returns in Scala |
127 //==================== |
346 //==================== |
128 |
347 |
129 //You should not use "return" in Scala: |
348 // You should not use "return" in Scala: |
130 // |
349 // |
131 // A return expression, when evaluated, abandons the |
350 // A return expression, when evaluated, abandons the |
132 // current computation and returns to the caller of the |
351 // current computation and returns to the caller of the |
133 // function in which return appears." |
352 // function in which return appears." |
134 |
353 |
135 def sq1(x: Int): Int = x * x |
354 def sq1(x: Int): Int = x * x |
136 def sq2(x: Int): Int = return x * x |
355 def sq2(x: Int): Int = return x * x |
137 |
356 |
138 def sumq(ls: List[Int]): Int = { |
357 def sumq(ls: List[Int]): Int = { |
139 (for (x <- ls) yield (return x * x)).sum[Int] |
358 ls.map(sq1).sum[Int] |
140 } |
359 } |
141 |
360 |
142 sumq(List(1,2,3,4)) |
361 sumq(List(1, 2, 3, 4)) |
143 |
362 |
144 |
363 |
145 // last expression in a function is the return statement |
364 |
146 def square(x: Int): Int = { |
365 def sumq(ls: List[Int]): Int = { |
147 println(s"The argument is ${x}.") |
366 val sqs : List[Int] = for (x <- ls) yield (return x * x) |
148 x * x |
367 sqs.sum |
149 } |
368 } |
|
369 |
150 |
370 |
151 |
371 |
152 |
372 |
153 // Pattern Matching |
373 // Pattern Matching |
154 //================== |
374 //================== |
209 |
428 |
210 // What happens if no case matches? |
429 // What happens if no case matches? |
211 |
430 |
212 println(season("foobar")) |
431 println(season("foobar")) |
213 |
432 |
|
433 |
214 // User-defined Datatypes |
434 // User-defined Datatypes |
215 //======================== |
435 //======================== |
216 |
436 |
217 abstract class Tree |
437 abstract class Colour |
218 case class Node(elem: Int, left: Tree, right: Tree) extends Tree |
438 case class Red() extends Colour |
219 case class Leaf() extends Tree |
439 case class Green() extends Colour |
220 |
440 case class Blue() extends Colour |
221 |
441 |
222 def insert(tr: Tree, n: Int): Tree = tr match { |
442 def fav_colour(c: Colour) : Boolean = c match { |
223 case Leaf() => Node(n, Leaf(), Leaf()) |
443 case Red() => false |
224 case Node(m, left, right) => |
444 case Green() => true |
225 if (n == m) Node(m, left, right) |
445 case Blue() => false |
226 else if (n < m) Node(m, insert(left, n), right) |
446 } |
227 else Node(m, left, insert(right, n)) |
447 |
228 } |
448 |
229 |
449 // actually this can be written with "object" |
230 |
450 |
231 val t1 = Node(4, Node(2, Leaf(), Leaf()), Node(7, Leaf(), Leaf())) |
|
232 insert(t1, 3) |
|
233 |
|
234 def depth(tr: Tree): Int = tr match { |
|
235 case Leaf() => 0 |
|
236 case Node(_, left, right) => 1 + List(depth(left), depth(right)).max |
|
237 } |
|
238 |
|
239 |
|
240 def balance(tr: Tree): Int = tr match { |
|
241 case Leaf() => 0 |
|
242 case Node(_, left, right) => depth(left) - depth(right) |
|
243 } |
|
244 |
|
245 balance(insert(t1, 3)) |
|
246 |
451 |
247 // another example |
452 // another example |
|
453 //================= |
248 |
454 |
249 abstract class Person |
455 abstract class Person |
250 case class King() extends Person |
456 case class King() extends Person |
251 case class Peer(deg: String, terr: String, succ: Int) extends Person |
457 case class Peer(deg: String, terr: String, succ: Int) extends Person |
252 case class Knight(name: String) extends Person |
458 case class Knight(name: String) extends Person |
253 case class Peasant(name: String) extends Person |
459 case class Peasant(name: String) extends Person |
254 case class Clown() extends Person |
460 |
255 |
461 |
256 def title(p: Person): String = p match { |
462 def title(p: Person): String = p match { |
257 case King() => "His Majesty the King" |
463 case King() => "His Majesty the King" |
258 case Peer(deg, terr, _) => s"The ${deg} of ${terr}" |
464 case Peer(deg, terr, _) => s"The ${deg} of ${terr}" |
259 case Knight(name) => s"Sir ${name}" |
465 case Knight(name) => s"Sir ${name}" |
260 case Peasant(name) => name |
466 case Peasant(name) => name |
261 } |
467 } |
|
468 |
262 |
469 |
263 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match { |
470 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match { |
264 case (King(), _) => true |
471 case (King(), _) => true |
265 case (Peer(_,_,_), Knight(_)) => true |
472 case (Peer(_,_,_), Knight(_)) => true |
266 case (Peer(_,_,_), Peasant(_)) => true |
473 case (Peer(_,_,_), Peasant(_)) => true |
279 |
486 |
280 println(people.sortWith(superior(_, _)).mkString(", ")) |
487 println(people.sortWith(superior(_, _)).mkString(", ")) |
281 |
488 |
282 |
489 |
283 |
490 |
284 // Higher-Order Functions |
491 |
285 //======================== |
492 |
286 |
493 |
287 // functions can take functions as arguments |
494 // Problems with mutability and parallel computations |
288 |
495 //==================================================== |
289 val lst = (1 to 10).toList |
496 |
290 |
497 def count_intersection(A: Set[Int], B: Set[Int]) : Int = { |
291 def even(x: Int): Boolean = x % 2 == 0 |
498 var count = 0 |
292 def odd(x: Int): Boolean = x % 2 == 1 |
499 for (x <- A; if (B contains x)) count += 1 |
293 |
500 count |
294 lst.filter(x => even(x)) |
501 } |
295 lst.filter(even(_)) |
502 |
296 lst.filter(even) |
503 val A = (1 to 1000).toSet |
297 |
504 val B = (1 to 1000 by 4).toSet |
298 lst.find(_ > 8) |
505 |
299 |
506 count_intersection(A, B) |
300 def square(x: Int): Int = x * x |
507 |
301 |
508 // but do not try to add .par to the for-loop above |
302 lst.map(square) |
509 |
303 |
510 |
304 lst.map(square).filter(_ > 4) |
511 //propper parallel version |
305 |
512 def count_intersection2(A: Set[Int], B: Set[Int]) : Int = |
306 lst.map(square).filter(_ > 4).map(square) |
513 A.par.count(x => B contains x) |
307 |
514 |
308 // in my collatz.scala |
515 count_intersection2(A, B) |
309 //(1 to bnd).map(i => (collatz(i), i)).maxBy(_._1) |
516 |
310 |
517 |
311 |
518 //for measuring time |
312 // type of functions, for example f: Int => Int |
519 def time_needed[T](n: Int, code: => T) = { |
313 |
520 val start = System.nanoTime() |
314 def my_map_int(lst: List[Int], f: Int => Int): List[Int] = lst match { |
521 for (i <- (0 to n)) code |
315 case Nil => Nil |
522 val end = System.nanoTime() |
316 case x::xs => f(x)::my_map_int(xs, f) |
523 (end - start) / 1.0e9 |
317 } |
524 } |
318 |
525 |
319 my_map_int(lst, square) |
526 val A = (1 to 1000000).toSet |
320 |
527 val B = (1 to 1000000 by 4).toSet |
321 // other function types |
528 |
322 // |
529 time_needed(10, count_intersection(A, B)) |
323 // f1: (Int, Int) => Int |
530 time_needed(10, count_intersection2(A, B)) |
324 // f2: List[String] => Option[Int] |
531 |
325 // ... |
532 |
326 |
|
327 |
|
328 def sumOf(f: Int => Int, lst: List[Int]): Int = lst match { |
|
329 case Nil => 0 |
|
330 case x::xs => f(x) + sumOf(f, xs) |
|
331 } |
|
332 |
|
333 def sum_squares(lst: List[Int]) = sumOf(square, lst) |
|
334 def sum_cubes(lst: List[Int]) = sumOf(x => x * x * x, lst) |
|
335 |
|
336 sum_squares(lst) |
|
337 sum_cubes(lst) |
|
338 |
|
339 // lets try it factorial |
|
340 def fact(n: Int): Int = ... |
|
341 |
|
342 def sum_fact(lst: List[Int]) = sumOf(fact, lst) |
|
343 sum_fact(lst) |
|
344 |
|
345 // Avoid being mutable |
|
346 //===================== |
|
347 |
|
348 // a student showed me... |
|
349 import scala.collection.mutable.ListBuffer |
|
350 |
|
351 |
|
352 |
|
353 def collatz_max(bnd: Long): (Long, Long) = { |
|
354 val colNos = ListBuffer[(Long, Long)]() |
|
355 for (i <- (1L to bnd).toList) colNos += ((collatz(i), i)) |
|
356 colNos.max |
|
357 } |
|
358 |
|
359 def collatz_max(bnd: Long): (Long, Long) = { |
|
360 (1L to bnd).map((i) => (collatz(i), i)).maxBy(_._1) |
|
361 } |
|
362 |
|
363 //views -> lazy collection |
|
364 def collatz_max(bnd: Long): (Long, Long) = { |
|
365 (1L to bnd).view.map((i) => (collatz(i), i)).maxBy(_._1) |
|
366 } |
|
367 |
|
368 // raises a GC exception |
|
369 (1 to 1000000000).filter(_ % 2 == 0).take(10).toList |
|
370 // ==> java.lang.OutOfMemoryError: GC overhead limit exceeded |
|
371 |
|
372 (1 to 1000000000).view.filter(_ % 2 == 0).take(10).toList |
|
373 |
533 |
374 |
534 |
375 |
535 |
376 // Sudoku |
536 // Sudoku |
377 //======== |
537 //======== |
397 |
557 |
398 val allValues = "123456789".toList |
558 val allValues = "123456789".toList |
399 val indexes = (0 to 8).toList |
559 val indexes = (0 to 8).toList |
400 |
560 |
401 |
561 |
402 |
|
403 |
|
404 def empty(game: String) = game.indexOf(EmptyValue) |
562 def empty(game: String) = game.indexOf(EmptyValue) |
405 def isDone(game: String) = empty(game) == -1 |
563 def isDone(game: String) = empty(game) == -1 |
406 def emptyPosition(game: String) = (empty(game) % MaxValue, empty(game) / MaxValue) |
564 def emptyPosition(game: String) = (empty(game) % MaxValue, empty(game) / MaxValue) |
407 |
565 |
408 |
566 |
409 def get_row(game: String, y: Int) = indexes.map(col => game(y * MaxValue + col)) |
567 def get_row(game: String, y: Int) = indexes.map(col => game(y * MaxValue + col)) |
410 def get_col(game: String, x: Int) = indexes.map(row => game(x + row * MaxValue)) |
568 def get_col(game: String, x: Int) = indexes.map(row => game(x + row * MaxValue)) |
|
569 |
|
570 get_row(game0, 3) |
|
571 get_col(game0, 0) |
411 |
572 |
412 def get_box(game: String, pos: Pos): List[Char] = { |
573 def get_box(game: String, pos: Pos): List[Char] = { |
413 def base(p: Int): Int = (p / 3) * 3 |
574 def base(p: Int): Int = (p / 3) * 3 |
414 val x0 = base(pos._1) |
575 val x0 = base(pos._1) |
415 val y0 = base(pos._2) |
576 val y0 = base(pos._2) |
416 val ys = (y0 until y0 + 3).toList |
577 val ys = (y0 until y0 + 3).toList |
417 (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue))) |
578 (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue))) |
418 } |
579 } |
419 |
580 |
420 |
581 get_box(game0, (0, 0)) |
421 //get_row(game0, 0) |
582 get_box(game0, (1, 1)) |
422 //get_row(game0, 1) |
583 get_box(game0, (2, 1)) |
423 //get_box(game0, (3,1)) |
584 |
424 |
585 // this is not mutable!! |
425 def update(game: String, pos: Int, value: Char): String = game.updated(pos, value) |
586 def update(game: String, pos: Int, value: Char): String = game.updated(pos, value) |
426 |
587 |
427 def toAvoid(game: String, pos: Pos): List[Char] = |
588 def toAvoid(game: String, pos: Pos): List[Char] = |
428 (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos)) |
589 (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos)) |
429 |
590 |
430 def candidates(game: String, pos: Pos): List[Char] = allValues diff toAvoid(game,pos) |
591 def candidates(game: String, pos: Pos): List[Char] = allValues.diff(toAvoid(game,pos)) |
431 |
592 |
432 //candidates(game0, (0,0)) |
593 //candidates(game0, (0,0)) |
433 |
594 |
434 def pretty(game: String): String = "\n" + (game sliding (MaxValue, MaxValue) mkString "\n") |
595 def pretty(game: String): String = |
|
596 "\n" + (game sliding (MaxValue, MaxValue) mkString "\n") |
435 |
597 |
436 def search(game: String): List[String] = { |
598 def search(game: String): List[String] = { |
437 if (isDone(game)) List(game) |
599 if (isDone(game)) List(game) |
438 else |
600 else { |
439 candidates(game, emptyPosition(game)).map(c => search(update(game, empty(game), c))).toList.flatten |
601 val cs = candidates(game, emptyPosition(game)) |
440 } |
602 cs.map(c => search(update(game, empty(game), c))).toList.flatten |
441 |
603 } |
|
604 } |
|
605 |
|
606 search(game0).map(pretty) |
442 |
607 |
443 val game1 = """23.915... |
608 val game1 = """23.915... |
444 |...2..54. |
609 |...2..54. |
445 |6.7...... |
610 |6.7...... |
446 |..1.....9 |
611 |..1.....9 |