parent
a9c3df78ee
commit
05e582ca3e
33
combinator/README.md
Normal file
33
combinator/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
layout: pattern
|
||||
title: Combinator
|
||||
folder: combinator
|
||||
permalink: /patterns/combinator/
|
||||
categories: Behavioral
|
||||
tags:
|
||||
- Functional
|
||||
- Reactive
|
||||
- Idiom
|
||||
---
|
||||
|
||||
## Also known as
|
||||
Composition pattern
|
||||
|
||||
## Intent
|
||||
The functional pattern representing a style of organizing libraries centered around the idea of combining functions.
|
||||
Putting it simply, there is some type T, some functions for constructing "primitive" values of type T,
|
||||
and some "combinators" which can combine values of type T in various ways to build up more complex values of type T.
|
||||
|
||||
|
||||
## Applicability
|
||||
Use the combinator pattern when:
|
||||
- You are able to create a more complex value from more plain values but having the same type(a combination of them)
|
||||
|
||||
## Real world examples
|
||||
- java.util.function.Function#compose
|
||||
- java.util.function.Function#andThen
|
||||
|
||||
## Credits
|
||||
- [Example for java](https://gtrefs.github.io/code/combinator-pattern/)
|
||||
- [Combinator pattern](https://wiki.haskell.org/Combinator_pattern)
|
||||
- [Combinatory logic](https://wiki.haskell.org/Combinatory_logic)
|
44
combinator/pom.xml
Normal file
44
combinator/pom.xml
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
|
||||
The MIT License
|
||||
Copyright © 2014-2019 Ilkka Seppälä
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
-->
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.iluwatar</groupId>
|
||||
<artifactId>java-design-patterns</artifactId>
|
||||
<version>1.23.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>combinator</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.combinator;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* The functional pattern representing a style of organizing libraries
|
||||
* centered around the idea of combining functions.
|
||||
* Putting it simply, there is some type T, some functions
|
||||
* for constructing "primitive" values of type T,
|
||||
* and some "combinators" which can combine values of type T
|
||||
* in various ways to build up more complex values of type T.
|
||||
* The class {@link Finder} defines a simple function {@link Finder#find(String)}
|
||||
* and connected functions
|
||||
* {@link Finder#or(Finder)},
|
||||
* {@link Finder#not(Finder)},
|
||||
* {@link Finder#and(Finder)}
|
||||
* Using them the became possible to get more complex functions {@link Finders}
|
||||
*/
|
||||
public class CombinatorApp {
|
||||
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CombinatorApp.class);
|
||||
|
||||
/**
|
||||
* main.
|
||||
* @param args args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
var queriesOr = new String[]{"many", "Annabel"};
|
||||
var finder = Finders.expandedFinder(queriesOr);
|
||||
var res = finder.find(text());
|
||||
LOGGER.info("the result of expanded(or) query[{}] is {}", queriesOr, res);
|
||||
|
||||
var queriesAnd = new String[]{"Annabel", "my"};
|
||||
finder = Finders.specializedFinder(queriesAnd);
|
||||
res = finder.find(text());
|
||||
LOGGER.info("the result of specialized(and) query[{}] is {}", queriesAnd, res);
|
||||
|
||||
finder = Finders.advancedFinder("it was","kingdom","sea");
|
||||
res = finder.find(text());
|
||||
LOGGER.info("the result of advanced query is {}", res);
|
||||
|
||||
res = Finders.filteredFinder(" was ", "many", "child").find(text());
|
||||
LOGGER.info("the result of filtered query is {}", res);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static String text() {
|
||||
return
|
||||
"It was many and many a year ago,\n"
|
||||
+ "In a kingdom by the sea,\n"
|
||||
+ "That a maiden there lived whom you may know\n"
|
||||
+ "By the name of ANNABEL LEE;\n"
|
||||
+ "And this maiden she lived with no other thought\n"
|
||||
+ "Than to love and be loved by me.\n"
|
||||
+ "I was a child and she was a child,\n"
|
||||
+ "In this kingdom by the sea;\n"
|
||||
+ "But we loved with a love that was more than love-\n"
|
||||
+ "I and my Annabel Lee;\n"
|
||||
+ "With a love that the winged seraphs of heaven\n"
|
||||
+ "Coveted her and me.";
|
||||
}
|
||||
|
||||
}
|
93
combinator/src/main/java/com/iluwatar/combinator/Finder.java
Normal file
93
combinator/src/main/java/com/iluwatar/combinator/Finder.java
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.combinator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Functional interface to find lines in text.
|
||||
*/
|
||||
public interface Finder {
|
||||
|
||||
/**
|
||||
* The function to find lines in text.
|
||||
* @param text full tet
|
||||
* @return result of searching
|
||||
*/
|
||||
List<String> find(String text);
|
||||
|
||||
/**
|
||||
* Simple implementation of function {@link #find(String)}.
|
||||
* @param word for searching
|
||||
* @return this
|
||||
*/
|
||||
static Finder contains(String word) {
|
||||
return txt -> Stream.of(txt.split("\n"))
|
||||
.filter(line -> line.toLowerCase().contains(word.toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* combinator not.
|
||||
* @param notFinder finder to combine
|
||||
* @return new finder including previous finders
|
||||
*/
|
||||
default Finder not(Finder notFinder) {
|
||||
return txt -> {
|
||||
List<String> res = this.find(txt);
|
||||
res.removeAll(notFinder.find(txt));
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* combinator or.
|
||||
* @param orFinder finder to combine
|
||||
* @return new finder including previous finders
|
||||
*/
|
||||
default Finder or(Finder orFinder) {
|
||||
return txt -> {
|
||||
List<String> res = this.find(txt);
|
||||
res.addAll(orFinder.find(txt));
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* combinator or.
|
||||
* @param andFinder finder to combine
|
||||
* @return new finder including previous finders
|
||||
*/
|
||||
default Finder and(Finder andFinder) {
|
||||
return
|
||||
txt -> this
|
||||
.find(txt)
|
||||
.stream()
|
||||
.flatMap(line -> andFinder.find(line).stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
103
combinator/src/main/java/com/iluwatar/combinator/Finders.java
Normal file
103
combinator/src/main/java/com/iluwatar/combinator/Finders.java
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.combinator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Complex finders consisting of simple finder.
|
||||
*/
|
||||
public class Finders {
|
||||
private Finders() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finder to find a complex query.
|
||||
* @param query to find
|
||||
* @param orQuery alternative to find
|
||||
* @param notQuery exclude from search
|
||||
* @return new finder
|
||||
*/
|
||||
public static Finder advancedFinder(String query, String orQuery, String notQuery) {
|
||||
return
|
||||
Finder.contains(query)
|
||||
.or(Finder.contains(orQuery))
|
||||
.not(Finder.contains(notQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtered finder looking a query with excluded queries as well.
|
||||
* @param query to find
|
||||
* @param excludeQueries to exclude
|
||||
* @return new finder
|
||||
*/
|
||||
public static Finder filteredFinder(String query, String... excludeQueries) {
|
||||
var finder = Finder.contains(query);
|
||||
|
||||
for (String q : excludeQueries) {
|
||||
finder = finder.not(Finder.contains(q));
|
||||
}
|
||||
return finder;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized query. Every next query is looked in previous result.
|
||||
* @param queries array with queries
|
||||
* @return new finder
|
||||
*/
|
||||
public static Finder specializedFinder(String... queries) {
|
||||
var finder = identMult();
|
||||
|
||||
for (String query : queries) {
|
||||
finder = finder.and(Finder.contains(query));
|
||||
}
|
||||
return finder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expanded query. Looking for alternatives.
|
||||
* @param queries array with queries.
|
||||
* @return new finder
|
||||
*/
|
||||
public static Finder expandedFinder(String... queries) {
|
||||
var finder = identSum();
|
||||
|
||||
for (String query : queries) {
|
||||
finder = finder.or(Finder.contains(query));
|
||||
}
|
||||
return finder;
|
||||
}
|
||||
|
||||
private static Finder identMult() {
|
||||
return txt -> Stream.of(txt.split("\n")).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static Finder identSum() {
|
||||
return txt -> new ArrayList<>();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.combinator;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class CombinatorAppTest {
|
||||
|
||||
@Test
|
||||
public void main() {
|
||||
CombinatorApp.main(new String[]{});
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.combinator;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class FinderTest {
|
||||
|
||||
@Test
|
||||
public void contains() {
|
||||
var example = "the first one \nthe second one \n";
|
||||
|
||||
var result = Finder.contains("second").find(example);
|
||||
Assert.assertEquals(result.size(),1);
|
||||
Assert.assertEquals(result.get(0),"the second one ");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* The MIT License
|
||||
* Copyright © 2014-2019 Ilkka Seppälä
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.iluwatar.combinator;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.iluwatar.combinator.Finders.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class FindersTest {
|
||||
|
||||
@Test
|
||||
public void advancedFinderTest() {
|
||||
var res = advancedFinder("it was","kingdom","sea").find(text());
|
||||
Assert.assertEquals(res.size(),1);
|
||||
Assert.assertEquals(res.get(0),"It was many and many a year ago,");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filteredFinderTest() {
|
||||
var res = filteredFinder(" was ", "many", "child").find(text());
|
||||
Assert.assertEquals(res.size(),1);
|
||||
Assert.assertEquals(res.get(0),"But we loved with a love that was more than love-");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specializedFinderTest() {
|
||||
var res = specializedFinder("love","heaven").find(text());
|
||||
Assert.assertEquals(res.size(),1);
|
||||
Assert.assertEquals(res.get(0),"With a love that the winged seraphs of heaven");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expandedFinderTest() {
|
||||
var res = expandedFinder("It was","kingdom").find(text());
|
||||
Assert.assertEquals(res.size(),3);
|
||||
Assert.assertEquals(res.get(0),"It was many and many a year ago,");
|
||||
Assert.assertEquals(res.get(1),"In a kingdom by the sea,");
|
||||
Assert.assertEquals(res.get(2),"In this kingdom by the sea;");
|
||||
}
|
||||
|
||||
|
||||
private String text(){
|
||||
return
|
||||
"It was many and many a year ago,\n"
|
||||
+ "In a kingdom by the sea,\n"
|
||||
+ "That a maiden there lived whom you may know\n"
|
||||
+ "By the name of ANNABEL LEE;\n"
|
||||
+ "And this maiden she lived with no other thought\n"
|
||||
+ "Than to love and be loved by me.\n"
|
||||
+ "I was a child and she was a child,\n"
|
||||
+ "In this kingdom by the sea;\n"
|
||||
+ "But we loved with a love that was more than love-\n"
|
||||
+ "I and my Annabel Lee;\n"
|
||||
+ "With a love that the winged seraphs of heaven\n"
|
||||
+ "Coveted her and me.";
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user