Zoals de oplettend GoT bezoeker in pagina 4&5 van dit topic ( [topic=196671] ) gelezen zal hebben, ben ik bezig met een implementatie van parser combinators in Java. Ik heb de niet lazy versie van de combinator library vrijwel werkend, maar dat heeft denk ik niet zo veel zin. Het moet natuurlijk lazy
. Omdat het andere topic nu wel erg uit de hand begon te lopen begin ik toch maar ff een nieuwe...
Ik heb een klein testje gemaakt voor lazy-evaluation. Het systeem is heel simpel, generiek en orthogonaal. Ik weet toch niet helemaal zeker of dit 'the way to go' is, dus ik ben benieuwd of iemand nog een betere oplossing ziet of commentaar heeft op mijn oplossing.
Het idee is heel simpel. De interface voor een functie is nog steeds hetzelfde.
Alles wat evalueerbaar moet zijn, moet de interface Lazy implementeren. Deze is generiek voor alle typen. De tweede methode is uiteraard alleen voor testdoeleinden.
Uiteindelijk moet je toch ergens beginnen, dus is er een generieke implementatie van Lazy, die voor alle typen werkt en dus echt een waarde heeft:
Ok, dan komt nu de lazy implementatie van plus
.
en de test klasse (het werkt btw
) :
Dat werkt dus allemaal lekker.
Nu vraag ik me af of dit de goede oplossing is. Je ziet duidelijk dat je op deze manier met oneindige lijsten kan werken. Wat ik echter niet zeker weet (en ik kon zo snel ff geen test verzinnen) is of dit ook gaat werken in de standaard tovertruk in lazy talen:
functie 1 krijgt als argument het resultaat mee functie 2
functie 2 krijgt als argument het resultaat mee functie 1
Dit is een imperatieve taal natuurlijk volkomen onzin. Zou dit kunnen met mijn lazy-evaluation implementatie (uiteraard alleen als de berekening ook wel echt mogelijk is)?
Weet iemand zo ff een werkend voorbeeldje van zo'n combinatie? Ik kan alleen maar voorbeelden bedenken die echt niet kunnen
.
Tips of kritiek wordt gewaardeerd
Ik heb een klein testje gemaakt voor lazy-evaluation. Het systeem is heel simpel, generiek en orthogonaal. Ik weet toch niet helemaal zeker of dit 'the way to go' is, dus ik ben benieuwd of iemand nog een betere oplossing ziet of commentaar heeft op mijn oplossing.
Het idee is heel simpel. De interface voor een functie is nog steeds hetzelfde.
code:
1
2
3
4
| public interface Function<A, R> { public R apply(A argument); } |
Alles wat evalueerbaar moet zijn, moet de interface Lazy implementeren. Deze is generiek voor alle typen. De tweede methode is uiteraard alleen voor testdoeleinden.
code:
1
2
3
4
5
| public interface Lazy<A> { public A evaluate(); public boolean isEvaluated(); } |
Uiteindelijk moet je toch ergens beginnen, dus is er een generieke implementatie van Lazy, die voor alle typen werkt en dus echt een waarde heeft:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| public class NotLazy<A> implements Lazy<A> { private A _value; public NotLazy(A value) { super(); _value = value; } public A evaluate() { return _value; } public boolean isEvaluated() { return true; } } |
Ok, dan komt nu de lazy implementatie van plus
code:
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
| public class Plus implements Function<Lazy<Integer>, Function<Lazy<Integer>,Lazy<Integer>>> { public Function<Lazy<Integer>, Lazy<Integer>> apply(Lazy<Integer> argument1) { return new AnonymousResult1(argument1); } private class AnonymousResult1 implements Function<Lazy<Integer>, Lazy<Integer>> { private Lazy<Integer> _argument1; public AnonymousResult1(Lazy<Integer> argument1) { _argument1 = argument1; } public Lazy<Integer> apply(Lazy<Integer> argument2) { return new AnonymousResult2(argument2); } private class AnonymousResult2 implements Lazy<Integer> { private Lazy<Integer> _argument2; private Integer _result; public AnonymousResult2(Lazy<Integer> argument2) { _argument2 = argument2; _result == null; } public Integer evaluate() { if(_result == null) { _result = new Integer(_argument1.evaluate().intValue() + _argument2.evaluate().intValue()); } return result; } public boolean isEvaluated() { return _result != null; } } } } |
en de test klasse (het werkt btw
code:
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
| public class LazyTest extends TestCase { public LazyTest(String name) { super(name); } public void testLazyPlus() { NotLazy<Integer> value1 = new NotLazy(new Integer(2)); NotLazy<Integer> value2 = new NotLazy(new Integer(3)); Lazy<Integer> value3 = new Plus().apply(value1).apply(value2); Lazy<Integer> value4 = new Plus().apply(value2).apply(value1); assertEquals("Plus result is 5", value3.evaluate(), new Integer(5)); assertTrue("Second not evaluated", !value4.isEvaluated()); } public static Test suite() { TestSuite suite = new TestSuite(LazyTest.class); return suite; } } |
Dat werkt dus allemaal lekker.
Nu vraag ik me af of dit de goede oplossing is. Je ziet duidelijk dat je op deze manier met oneindige lijsten kan werken. Wat ik echter niet zeker weet (en ik kon zo snel ff geen test verzinnen) is of dit ook gaat werken in de standaard tovertruk in lazy talen:
functie 1 krijgt als argument het resultaat mee functie 2
functie 2 krijgt als argument het resultaat mee functie 1
Dit is een imperatieve taal natuurlijk volkomen onzin. Zou dit kunnen met mijn lazy-evaluation implementatie (uiteraard alleen als de berekening ook wel echt mogelijk is)?
Weet iemand zo ff een werkend voorbeeldje van zo'n combinatie? Ik kan alleen maar voorbeelden bedenken die echt niet kunnen
Tips of kritiek wordt gewaardeerd
Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment