fj.data.LazyString Maven / Gradle / Ivy
package fj.data;
import fj.*;
import static fj.Function.compose;
import static fj.Function.curry;
import static fj.P.p;
import static fj.data.Option.none;
import static fj.data.Option.some;
import static fj.data.Stream.join;
import static fj.function.Booleans.or;
import static fj.function.Characters.isSpaceChar;
import static fj.Equal.charEqual;
import static fj.Equal.streamEqual;
import java.util.regex.Pattern;
/**
* A lazy (non-evaluated) immutable character string.
*/
public final class LazyString implements CharSequence {
private final Stream s;
private LazyString(final Stream s) {
this.s = s;
}
/**
* Constructs a lazy string from a String.
*
* @param s A string from which to construct a lazy string.
* @return A lazy string with the characters from the given string.
*/
public static LazyString str(final String s) {
return new LazyString(Stream.unfold(o -> {
final String s2 = o._1();
final int n = o._2();
final Option>> none = none();
return s2.length() <= n ? none : some(p(s2.charAt(n), p(s2, n + 1)));
}, p(s, 0)));
}
/**
* The empty string.
*/
public static final LazyString empty = str("");
/**
* Constructs a lazy string from a stream of characters.
*
* @param s A stream of characters.
* @return A lazy string with the characters from the given stream.
*/
public static LazyString fromStream(final Stream s) {
return new LazyString(s);
}
/**
* Gives a stream representation of this lazy string.
*
* @return A stream representation of this lazy string.
*/
public Stream toStream() {
return s;
}
/**
* The length of the lazy string. Note that this operation is O(n).
*
* @return The length of this lazy string.
*/
public int length() {
return s.length();
}
/**
* Returns the caracter at the specified index.
*
* @param index The index for the character to be returned.
* @return The character at the specified index.
*/
public char charAt(final int index) {
return s.index(index);
}
/**
* Gets the specified subsequence of this lazy string.
* This operation does not fail for indexes that are out of bounds. If the start index is past the end
* of this lazy string, then the resulting character sequence will be empty. If the end index is past the
* end of this lazy string, then the resulting character sequence will be truncated.
*
* @param start The character index of this lazy string at which to start the subsequence.
* @param end The character index of this lazy string at which to end the subsequence.
* @return A character sequence containing the specified character subsequence.
*/
public CharSequence subSequence(final int start, final int end) {
return fromStream(s.drop(start).take(end - start));
}
/**
* Returns the String representation of this lazy string.
*
* @return The String representation of this lazy string.
*/
public String toStringEager() {
final StringBuilder builder = new StringBuilder(length() + 16);
s.foreachDoEffect(c -> builder.append(c.charValue()));
return builder.toString();
}
public String toStringLazy() {
return s.isEmpty() ? "" : "LazyString(" + Show.charShow.showS(s.head()) + ", ?)";
}
@Override
public String toString() {
return toStringLazy();
}
public String eval() {
return toStringEager();
}
/**
* Appends the given lazy string to the end of this lazy string.
*
* @param cs A lazy string to append to this one.
* @return A new lazy string that is the concatenation of this string and the given string.
*/
public LazyString append(final LazyString cs) {
return fromStream(s.append(cs.s));
}
/**
* Appends the given String to the end of this lazy string.
*
* @param s A String to append to this lazy string.
* @return A new lazy string that is the concatenation of this lazy string and the given string.
*/
public LazyString append(final String s) {
return append(str(s));
}
/**
* Returns true if the given lazy string is a substring of this lazy string.
*
* @param cs A substring to find in this lazy string.
* @return True if the given string is a substring of this string, otherwise False.
*/
public boolean contains(final LazyString cs) {
return or(s.tails().map(compose(startsWith().f(cs), fromStream)));
}
/**
* Returns true if the given lazy string is a suffix of this lazy string.
*
* @param cs A string to find at the end of this lazy string.
* @return True if the given string is a suffix of this lazy string, otherwise False.
*/
public boolean endsWith(final LazyString cs) {
return reverse().startsWith(cs.reverse());
}
/**
* Returns true if the given lazy string is a prefix of this lazy string.
*
* @param cs A string to find at the start of this lazy string.
* @return True if the given string is a prefix of this lazy string, otherwise False.
*/
public boolean startsWith(final LazyString cs) {
return cs.isEmpty() || !isEmpty() && charEqual.eq(head(), cs.head()) && tail().startsWith(cs.tail());
}
/**
* First-class prefix check.
*
* @return A function that yields true if the first argument is a prefix of the second.
*/
public static F> startsWith() {
return curry((needle, haystack) -> haystack.startsWith(needle));
}
/**
* Returns the first character of this string.
*
* @return The first character of this string, or error if the string is empty.
*/
public char head() {
return s.head();
}
/**
* Returns all but the first character of this string.
*
* @return All but the first character of this string, or error if the string is empty.
*/
public LazyString tail() {
return fromStream(s.tail()._1());
}
/**
* Checks if this string is empty.
*
* @return True if there are no characters in this string, otherwise False.
*/
public boolean isEmpty() {
return s.isEmpty();
}
/**
* Returns the reverse of this string.
*
* @return the reverse of this string.
*/
public LazyString reverse() {
return fromStream(s.reverse());
}
/**
* Returns the first index of the given character in this lazy string, if present.
*
* @param c A character to find in this lazy string.
* @return The first index of the given character in this lazy string, or None if the character is not present.
*/
public Option indexOf(final char c) {
return s.indexOf(Equal.charEqual.eq(c));
}
/**
* Returns the first index of the given substring in this lazy string, if present.
*
* @param cs A substring to find in this lazy string.
* @return The first index of the given substring in this lazy string, or None if there is no such substring.
*/
public Option indexOf(final LazyString cs) {
return s.substreams().indexOf(eqS.eq(cs.s));
}
/**
* Regular expression pattern matching.
*
* @param regex A regular expression to match this lazy string.
* @return True if this string mathches the given regular expression, otherwise False.
*/
public boolean matches(final String regex) {
return Pattern.matches(regex, this);
}
/**
* Splits this lazy string by characters matching the given predicate.
*
* @param p A predicate that matches characters to be considered delimiters.
* @return A stream of the substrings in this lazy string, when separated by the given predicate.
*/
public Stream split(final F p) {
final Stream findIt = s.dropWhile(p);
final P2, Stream> ws = findIt.split(p);
return findIt.isEmpty() ? Stream.nil()
: Stream.cons(fromStream(ws._1()), () -> fromStream(ws._2()).split(p));
}
public LazyString map(F f) {
return fromStream(s.map(f));
}
public LazyString bind(F f) {
return fromStream(s.bind(c -> f.f(c).toStream()));
}
/**
* Splits this lazy string by the given delimiter character.
*
* @param c A delimiter character at which to split.
* @return A stream of substrings of this lazy string, when separated by the given delimiter.
*/
public Stream split(final char c) {
return split(charEqual.eq(c));
}
/**
* Splits this lazy string into words by spaces.
*
* @return A stream of the words in this lazy string, when split by spaces.
*/
public Stream words() {
return split(isSpaceChar);
}
/**
* Splits this lazy string into lines.
*
* @return A stream of the lines in this lazy string, when split by newlines.
*/
public Stream lines() {
return split('\n');
}
public static F> lines_() {
return LazyString::lines;
}
/**
* Joins the given stream of lazy strings into one, separated by newlines.
*
* @param str A stream of lazy strings to join by newlines.
* @return A new lazy string, consisting of the given strings separated by newlines.
*/
public static LazyString unlines(final Stream str) {
return fromStream(join(str.intersperse(str("\n")).map(toStream)));
}
public static F, LazyString> unlines_() {
return LazyString::unlines;
}
/**
* Joins the given stream of lazy strings into one, separated by spaces.
*
* @param str A stream of lazy strings to join by spaces.
* @return A new lazy string, consisting of the given strings with spaces in between.
*/
public static LazyString unwords(final Stream str) {
return fromStream(join(str.intersperse(str(" ")).map(toStream)));
}
/**
* First-class conversion from lazy strings to streams.
*/
public static final F> toStream =
LazyString::toStream;
/**
* First-class conversion from lazy strings to String.
*/
public static final F toString =
LazyString::toString;
/**
* First-class conversion from character streams to lazy strings.
*/
public static final F, LazyString> fromStream =
LazyString::fromStream;
private static final Equal> eqS = streamEqual(charEqual);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy