xpresso

The pythonic way to code in Java.

xpresso is a Java library inspired by Python. It allows a (near) line-into-line rewrite of a Python code into Java. It's also a great way to prototype your algorithms directly in Java.

xpresso implements in Java familiar pythonic methods (e.g., len, enumerate, split/join, slicing) and coding paradigms (e.g., everything is iterable, list comprehensions, generators, lambda expressions, filtering iterables using predicates and modifying them using functions).

xpresso also offers multiple useful tools, usually one-liners, that save developer's time and make the code more readable: x.String, x.Object, x.Function, x.memo, x.WebService, x.MapReduce, x.go, x.timer, x.Json, x.mysql, x.csv and others.

xpresso: less boilerplate, more fun, more work done.

License: [MIT] (https://en.wikipedia.org/wikei/MIT_License).

Usage

import com.wantedtech.common.xpresso.x;

x.print("Hello World!");

Main features

Types similar to pythonic ones

import com.wantedtech.common.xpresso.types.*;

Imports: set, dict, list, tuple, DefaultDict, OrderedDict, Bag, HappyFile, HappySQL

Slicable and iterable str type

str city = x.str("New York City");

x.print(city.slice(4,8));

Console: York
for (String character : city)
    x.print(character);

Console: N
e
w

Y
o
r
k

One-line file open

Python:

with open("name.txt","r","utf-8") as f:
    #do stuff

xpresso:

try (HappyFile f = x.open("name.txt","r","utf-8")) {
    //do stuff
}

Works for write/read/append in both text and binary mode.

As in Python, a file opened for reading in text mode is an Iterable of strings:

Python:

for line in f:
    print line

xpresso:

for (String line : f)
    x.print(line);

Tuples

Python:

my_car = ("Honda", "red", 2010, True)

xpresso:

tuple myCar = x.tuple("Honda", "red", 2010, true);

Dynamic name assignment to tuple elements:

myCar.name("make","color","year","good");
x.print(myCar.get("good"),myCar.get("make"),myCar.get("year"));

Console: true Honda 2010

If name method has not yet been called, but get(someName) is called for the first time, then the returned value will be get(i), where i is the smallest index of a remaining unnamed element in the tuple. All the subsequent calls for the same value someName, the same element i will be returned by get(someName).

You can also define and use a typed version of tuple. For example:

tuple3<String,String,Integer> myCar = x.tuple3("Honda", "red", 2010);

String myCarMake = myCar.left;
String myCarColor = myCar.middle;
Integer myCarYear = myCar.right;

tuple2<String,tuple3<String,String,Integer>> item = x.tuple2("car",myCar);

String type = item.key; //or, alternatively String type = item.left;
tuple3<String,String,Integer> car = item.value; //or, alternatively tuple3<String,String,Integer> car = item.right;

Neat standard object creation

Python:

trips = ["Dubai","New York","London","Paris","Moscow","London","Saint-Petersburg","New York"]

russian_cities = set(["Moscow","Saint-Petersburg"])

rank = dict(("Moscow":30),("Saint-Petersburg":15),("New York":20),("London":10),("Paris":5),("Dubai":32))

xpresso:

list<String> trips = x.list("Dubai","New York","London","Paris","Moscow","London","Saint-Petersburg","New York");

set<String> russianCities = x.set("Moscow","Saint-Petersburg");

dict<Integer> rank = x.dict(x.tuple("Moscow",30),x.tuple("Saint-Petersburg",15),x.tuple("New York",20),x.tuple("London",10),x.tuple("Paris",5),x.tuple("Dubai",32));

Functions and predicates

import com.wantedtech.common.functional.*

Function<Object, String> toUpperCaseFun = new Function<Object, String>() {
    public String apply(Object value) {
        return value.toString().toUpperCase();
    }
};

list<String> tripsUp = x.map(toUpperCaseFun, trips);
x.print(tripsUp);

Console: [DUBAI, NEW YORK, LONDON, PARIS, MOSCOW, LONDON, SAINT-PETERSBURG, NEW YORK]
Predicate<Object> containsO = new Predicate<Object>() {
    public Boolean apply(Object value) {
        return x.String("o").in(value.toString()) ? true : false;
    }
};

list<String> tripsO = x.filter(containsO, trips);
x.print(tripsO);

Console: [New York, London, Moscow, London, New York]

You don't need to define a new Function class every time you want to transform an iterable in a certain way. You can use the x.Function that automagically wraps any static method of any Java class into a Function:

Function<String,String> myUpper = x.Function(String.class, "toUpperCase");

iterable = x.map(myUpper, iterable);

The x.Function method can also wrap static methods that take several arguments:

Function<tuple3<String,Integer,Boolean>,Double> mySomeFunc = x.Function(Some.class, "someStaticMethod");

Function<tuple3<String,Integer,Boolean>,Double> myOtherFunc = x.Function(Other.class, "someOtherMethod");

Function<tuple3<String,Integer,Boolean>,Double> funcToUse;
if (someCondition) {
    funcToUse = mySomeFunc;
} else {
    funcToUse = myOtherFunc;
}

double sum;
for (element : iterable) {
    sum += funcToUse.apply(x.tuple3(element,intParam,boolParam));
}

If in a certain class there're more than one static method with the same name, you need to specify which one of them you want to wrap by providing parameter types:

Function<tuple3<String,Integer,Boolean>,Double> mySomeFunc = x.Function(Some.class, "someStaticMethod", String.class, Integer.class, Boolean.class);

Lambda expressions

Python:

best_cities = reversed(sorted(item for item in rank.items(),lambda x: x[0]))

xpresso:

list<String> bestCities = x.reverse(x.sort(yield().forEach(rank.items()),x.lambdaF("x: x[0]")));

More complex lambda expressions:

Predicate<Object> pr = x.lambdaP("x : f0(f1(x[1])) == '''new york'''",x.lower,x.strip);
Function<Object,Integer> squareFun = x.lambdaF("x : x * x");

Function<Object,Integer> fun = x.lambdaF("x : x[0] * 10 * (x[1] - f0(x[2])))",squareFun);

Function chains:

Function<Object,Integer> incrementFun = x.lambdaF("x : x + 1");
Function<Object,Integer> squareFun = x.lambdaF("x : x * x");

Function<Object,Integer> chainFun = x.chain(incrementFun,squareFun);

chainFun will first increment, then square its input. x.chain(...) can take more than two functions as argument. The last function in the chain has to return the value of the desired output type.

List comprehensions

Python:

foreign_trips_lower = [city.lower() for city in trips if city not in russian_cities]

xpresso:

list<String> foreignTripsLower = x.list(x.<String>yield().apply(x.lower).forEach(trips).unless(x.in(russianCities)));

Python:

cool_cities = dict([(city.upper(),true) for (city, score) in rank.items() if score > 5])

xpresso:

dict<Integer> coolCities = x.dict(x.yield("city","_").apply(x.upper).replace(true).where("city","score").in(rank.items()).when(x.lambdaP("city, score : score > 20")));

Python:

evals = [True if value == "good" else False for value in some_list]

xpresso:

list<Boolean> evals = x.list(x.<Boolean>yield().replace(true).when(x.lambdaP("x : x == '''good'''")).replaceOtherwise(false).forEach(someList));

You can use list comprehensions to extract properties from element objects:

class PlannedTrip {
    int year;
    String city;
    
    public PlannedTrip(int year, String city) {
        this.year = year;
        this.city = city;
    }
 
    public int getYear() { return year; }
    public String getCity() { return city; }
}

list<PlannedTrip> plans = x.list(new PlannedTrip(2015, "Moscow"), new PlannedTrip(2016, "Paris"));

list<tuple> plansData = x.list(x.yield("year", "city").where("year", "city").in(plans));

x.print(plansData);

Console: [(2015, Moscow), (2016, Paris)]

You can also filter the extracted values in the same expression:

list<tuple> plansData = x.list(x.yield("year", "city").where("year", "city").in(plans).when(x.lambdaP("year, city : year > 2015)));

x.print(plansData);

Console: [(2016, Paris)]

RESTful web services

Let's suppose we have an object of a class SomeMath which has two methods we would like to publish on the network as RESTful web services, getSum and getProduct:

public class SomeMath() {
    public Double getSum(Double[] values) { //we want to publish this one
        return x.sum(values);
    }
    public Double getProduct(Double x, Double y) {//and this one
        return x * y;
    }
    public Double anotherMethod(Double somethingElse) {//but not this one
        return somethingElse;
    }
}

In order to convert our SomeMath class into a web service, we simply need to first annotate our two methods we want to call from the network with the @ExposeAs annotation, and then start our web service:

public class SomeMath() {
    public Double getSum(@ExposeAs("values") Double[] values) {
        return x.sum(values);
    }
    public Double getProduct(@ExposeAs("x") Double x, @ExposeAs("y") Double y) {
        return x * y;
    }
    public Double anotherMethod(Double somethingElse) {
        return somethingElse;
    }
}

WebService ws = x.WebService(new SomeMath(), 8080).start();

That's all! Our web service is up and running. Let's test it. Open the following url in your browser:

http://localhost:8080/SomeMath/getSum?values=5&values=6&values=7

The output:

18.0

Now open the following url:

http://localhost:8080/SomeMath/getProduct?x=5&y=10

The output:

50.0

If a method returns an output type of more complex classes such as Java's standard Map and List, or xpresso's own list and dict, the output will be a corresponding JSON string.

Generators

Python:

def firstn(n):
    num = 0
    while num < n:
        yield num
        num += 1

for i in firstn(500000):
    print i

xpresso:

public Generator<Integer> firstn (final int n) {
    return new Generator<Integer>() {
        public void generate() {
            int num = 0;
            while (num < n) {
                yield(num);
                num++;
            }
        }
    };
}

for (int i : firstn(500000)) 
    x.print(i);

Memoization

As a quick example, let xerox be a Function object whose method apply copies the string "hello" the given number count of times:

Function<Integer, String> xerox = new Function<Integer, String>() {
    public String apply(Integer count) {
        return x.String("hello").times(count);
    }
};

It's a long to execute function for large values of count.

In order to avoid the long computation for the same value of count, we first create a cached version of xerox using x.memo:

Function<Integer,String> cachedXerox = x.memo(xerox);

The first call of the function. The computation takes a very long time:

x.timer.start();
String copies = cachedXerox.apply(5000000);
x.print(x.timer.stop());

Console: 18.898s

The second call with the same value of count, the result is instantaneous:

x.timer.start();
String moreCopies = cachedXerox.apply(5000000);
x.print(x.timer.stop());

Console: 0.0s

x.memo can be used to cache methods of object of any Java type, not only Function. Notice the usage of the standard x.timer: no additional timer object needs to be created.

Concurrency (beta)

Concurrency in xpresso is inspired by Go and, as a consequence, is extremely simple. First, define a worker as an instance of Predicate:

Predicate<Channel<Integer>> worker = new Predicate<Channel<Integer>>() {
    public Boolean apply(Channel<Integer> channel) {
        while (some_condition_true) {
            Integer value = computeValue();   //compute something in parallel
            channel.send(value);          //send the computed value to the channel
        }
        return true;                       //everything went as expected
    }
};

Then, define the channel to where the workers should send the computed values as soon as those values are ready:

Channel<Integer> channel = x.Channel(Integer.class);//this channel only accepts Integer values

Then, start as many concurrent workers as needed:

x.go(worker, channel);
x.go(worker, channel);
x.go(worker, channel);
...

Finally, retrieve from the channel the values concurrently computed by the workers when those values are needed:

for (Integer value : channel) {
    x.print(value);
}

MapReduce (beta)

Let's assume that we have a list of elements we want to process:

list<String> elements = x.list("Map","aNd","ReDuce","arE","aWEsome");

The processing of each element takes a long time (10 seconds), so we want to parallelize the processing on our multicore machine. Let the processing be as follows: if the element starts with an "a", then put it in uppercase and join it with other uppercase elements using "~" as separator; if the element doesn't start with an "a", then put it to lowercase and join it with other lowercase words.

Let's define the Mapper and Reducer:

import com.wantedtech.common.xpresso.experimental.concurrency.Mapper;
import com.wantedtech.common.xpresso.experimental.concurrency.Reducer;

static Mapper<String,String> mapper = new Mapper<String,String>() {
    public void map(String input) {
        x.Time.sleep(10); //the processing of each element takes a long time :-)
        if (x.String(input).startsWith("a")) {
            yield("upper", input.toUpperCase());                
        } else {
            yield("lower", input.toLowerCase());
        }
    }
};

static Reducer<String,String> reducer = new Reducer<String,String>() {
    public void reduce(tuple2<String,list<String>> input) {
        yield(input.key,x.String("~").join(input.value));
    }
};

Our mapper does the transformation of the string case as described above, and our reducer joins the resulting values with the "~".

Our MapReduce setup is now ready, so let's start crunching:

x.timer.start();
x.print(x.<String,String,String>MapReduce(elements).map(mapper).reduce(reducer), x.timer.stop());

Console:
{upper:AND~AWESOME~ARE, lower:reduce~map}
10.013s

As you can see, the processing of all 5 elements took only about 10 seconds, while we have defined above that the processing of each single element takes 10 seconds.

JSON

Remember the rank dict:

dict<Integer> rank = x.dict(x.tuple("Moscow",30),x.tuple("Saint-Petersburg",15),x.tuple("New York",20),x.tuple("London",10),x.tuple("Paris",5),x.tuple("Dubai",32));

Let's first dump it as a String:

String rankAsString = x.Json(rank).toString();
x.print(rankAsString);

Console: {"New York":20,"London":10,"Saint-Petersburg":15,"Moscow":30,"Dubai":32,"Paris":5}

Now let's create a copy of the rank dict from its JSON string representation:

dict<Integer> rankCopy = x.String(rankAsString).parseJson();

Compare the original rank dict to the copy:

x.print(x.Object(rank).equals(rankCopy));

Console: true

CSV

Read from file:

try (HappyFile f = x.open("filename.txt","r","utf-8")) {
    for (list<String> row : x.csv(f)) {
        //do stuff
    }
}

Or, simply:

list<list<String>> data = x.list(x.csv("filename.txt","r","utf-8"));

Write to file:

try (HappyFile f = x.open("filename.txt","w","utf-8")) {
    for (list<?> row : data){
        x.csv(f).writerow(row);
    }
}

Or, simply:

try (HappyFile f = x.open("filename.txt","w","utf-8")) {
    f.write(x.csv(data).toString());
}

Write to a StringBuilder:

StringBuilder builder = new StringBuilder();

for (list<?> row : data) {
    x.csv(builder).writerow(row);
}

String cs = c.toString();

Or, simply:

String cs = x.csv(data).toString();

MySQL

String host = "host:port";
String user = "user";
String password = "password";
String db = "db";

try (HappySQL sql = x.mysql(host, user, password, db)) {
    try (HappySQL sql2 = x.mysql(sql)){
        String query =    "SELECT ID FROM " +
                        "tbl_Employees e " +
                        "WHERE e.Name LIKE ?";
        for (tuple row : sql.execute(query, "John %")) {
            query =   "UPDATE tbl_Employees " +
                    "SET Promoted = 1 " +
                    "WHERE ID = ?";
            sql2.execute(query, row.get("ID"));
        }
    }
}

Extended String functions

Python:

if "e" in "Hello World":
    #do stuff

xpresso:

if(x.String("e").in("Hello World")) {
    //do stuff
}

Python:

colorsPattern = "|".join(["black","green","red","white"]);

print colorsPattern

>>> black|green|red|white

xpresso:

String colorsPattern = x.String("|").join(x.list("black","green","red","white"));

x.print(colorsPattern);

Console: black|green|red|white

Python:

tokens = "Moscow;London;Paris".split(";")

print tokens

>>> ['Moscow', 'London', 'Paris']

xpresso:

list<String> tokens = x.String("Moscow;London;Paris").split(";");

x.print(tokens);

Console: [Moscow, London, Paris]

Transliterate:

String trans = x.String("Чичётка 北亰").translit();

x.print(trans);

Console: Čičëtka bei jing

x.print(trans.stripAccents());

Console: Cicetka bei jing

Convert unicode to ascii:

String unidec = x.String("Чичётка 北亰").unidecode();

x.print(unidec);

Console: Chichiotka bei jing

Approximate string comparison:

x.print(x.String("Hello World").similarity("Hello Wold!"))

Console: 91

The output is 100% compatible with FuzzyWuzzy.

Approximate pattern matching:

x.print(x.String("You are cooding in Java.").search("coding"));

Console: 8

Get similar strings:

list<String> lookAlikes = x.String("apple").lookAlikes(x.list("ape", "apples", "peach", "puppy"),50);

x.print(lookAlikes);

Console: [ape, apples]

Tokenization:

String text = "English is hard. It can be understood through tough thorough thought, though.";

for (Sentence s : x.String.EN.tokenize(text)) {
    for (Token t : s) {
        x.print(t);
    }
}

Console: English
is
hard
.
It
can
...

Part-of-speech tagging

With xpresso you can easily POS tag any English text with the very fast and accurate (~97%) Stanford CoreNLP english-left3words model:

import com.wantedtech.common.xpresso.sentence.Sentence;
import com.wantedtech.common.xpresso.sentence.PosTagger;
import com.wantedtech.common.xpresso.sentence.pos.en.stanford.MaxentPosTagger;

PosTagger posTagger = new MaxentPosTagger();
String text = "Some English text. Multiple sentences.";
for (Sentence sent : x.String.EN.tokenize(text)) {
    posTagger.tag(sent);
    x.print(sent.getAnnotations("pos"));
}

Console: [(Some, DT), (English, NNP), (text, NN), (., .)]
[(Multiple, JJ), (sentences, NNS), (., .)]

Slicing for list, String, and str

Python:

trips = ["Dubai","New York","London","Paris","Moscow","London","Saint-Petersburg","New York"]

print trips[2:4]

>>> ['London', 'Paris']

xpresso:

x.print(trips.slice(2,4));

Console: [London, Paris]

Python:

print trips[:5]

>>> ['Dubai','New York','London','Paris','Moscow']

xpresso:

x.print(trips.sliceTo(5));

Console: [Dubai, New York, London, Paris, Moscow]

Negative and non-unit steps are supported:

Python:

print trips[::-1]

>>> ['New York', 'Saint-Petersburg', 'London', 'Moscow', 'Paris', 'London', 'New York', 'Dubai']

xpresso:

x.print(trips.slice(-1));

Console: [New York, Saint-Petersburg, London, Moscow, Paris, London, New York, Dubai]

Python:

print trips[::2]

>>> ['Dubai','London','Moscow','Saint-Petersburg']

xpresso:

x.print(trips.slice(2));

Console: [Dubai, London, Moscow, Saint-Petersburg]

Slicer object

Slicer LAST_THREE = x.sliceFrom(-3);

x.print(x.String("tic tac toe").slice(LAST_THREE));

Console: toe

Iterable regex search results

Python:

for long_word_match in re.finditer("\b\w{10,}\b",text):
    print long_word_match.group(0)

xpresso:

for (Match longWordMatch : x.Regex("\\b\\w{10,}\\b").findIter(text))
    x.print(longWordMatch.group(0));

Python:

for long_word in re.findall("\b\w{10,}\b",text):
    print long_word

xpresso:

for (String longWord : x.Regex("\\b\\w{10,}\\b").findAll(text))
    x.print(longWord);

Replace with a Function

Python:

def toUpperCaseFun(value):
    return value.group(0).upper()

text = re.sub("\b\w{10,}\b",toUpperCaseFun,text)

xpresso:

Function<Match,String> toUpperCaseFun = new Function<Match,String>(){
    public String apply(Match value) {
        return value.group(0).toUpperCase();
    }
}

text = x.Regex("\\b\\w{10,}\\b").sub(toUpperCaseFun,text);

Replace with a dict

dict<String> replacer = x.dict(x.tuple("bad","good"),x.tuple("small","big"),x.tuple("hard","easy"));

text = x.Regex(replacer).sub(text);

Predefined regex patterns

list<String> emails = x.list(x.Regex.EMAIL.findAll("Contact me at john.smith@company.com or john@smith.com."));
x.print(emails);

Console: [smith@company.com, john@smith.com]

Other patterns include x.Regex.LINK, x.Regex.EMAIL, x.Regex.IPV4, x.Regex IPV6, x.Regex.HEX_COLOR, x.Regex.ACRONYM, x.Regex.CREDIT_CARD, x.Regex.FLOAT, and so on, as well as a number of country-specific ones: x.Regex.US.DATE, x.Regex.US.TIME, x.Regex.US.PHONE, x.Regex.US.PRICE, x.Regex.US.STREET_ADDRESS, etc.

The Token type

Token tok = x.Token("MySQL5");
x.print(tok.shape(), tok.isCamel(), tok.hasDigits(), tok.hasRussian());

Console: ULUUUD, true, true, false
tok = x.Token("Thinking");
x.print(tok.stem());

Console: Think

hashCode(), equals(...), and compareTo(...) builders

When defining a class:

@Override
int hashCode() {
    return x.Object(this).hashCode();
}

In the above code, xpresso first finds the members of this (via reflections) and then dynamically computes the hash code for this based on the values of its members.

@Override
boolean equals(Object obj) {
    return x.Object(this).equals(obj);
}

In the above code, xpresso first finds the members of the two objects (this and obj), and then compares the values of those members.

@Override
public int compareTo(Object obj) {
    return x.Object(this).compareTo(obj, fieldName0, fieldName1, ...);
}

In the above code, xpresso first finds the members of the two objects (this and obj). It then compares the values of those members between the two objects if those members' names are listed among the input field names fieldName0, fieldName1, etc. The order of comparisons between the member's values is the same as the order of input field names.

Assertions

x.assertTrue(condition);    // throws IllegalArgumentException

x.assertNotNull(parameter);   // throws NullPointerException

x.assertNotEmpty(iterable);   /* throws NullPointerException if iterable is null,
                            throws IllegalArgumentException if iterable is empty */
x.assertNotEmpty(string);

x.assertNotEmpty(array);

Built-in iterators

  • cycle
for (String letter : x.cycle(x.str("ABC")))
    x.print(letter);

Console: A
B
C
A
B
C
A
B
C
...
for (String letter : x.cycle(x.list("hello","world"),3))
    x.print(letter);

Console: hello
world
hello
world
hello
world
  • repeat
for (String word : x.repeat("cool"))
    x.print(word);

Console: cool
cool
cool
cool
...
for (String word : x.repeat("cool",3))
    x.print(word);

Console: cool
cool
cool
  • count
for (Integer index : x.countTo(3))
    x.print(index);

Console: 0
1
2
for (Integer index : x.countFrom(10))
    x.print(index);

Console: 10
11
12
13
...
for (Integer index : x.count(3,10))
    x.print(index);

Console: 3
4
5
6
7
8
9
for (Integer index : x.count(3,10,3))
    x.print(index);

Console: 3
6
9

x.count(min, max) and x.count(min, max, step) replace Python's range(min, max) and range(min, max, step).

Print anything

x.print("Hello World", 1, true, x.list(1, 2, 3), null);

Console: Hello World 1 true [1, 2, 3] NullType

N-grams

str phrase = "If you want something done right, you have to do it yourself.";

list<str> tokens = phrase.split();

list<list<str>> ngrams = tokens.ngrams(3);

x.print(ngrams);

Console: [[If, you, want], [you, want, something], [something, done, right]..., [do, it, yourself.]]

largestN and smallestN

list<String> cities = x.list("Moscow","San Francisco","Saint-Petersbourg","Rome");

x.print(x.smallestN(cities,2));

Console: [Rome, Moscow]
list<String> cities = x.list("Moscow","San Francisco","Saint-Petersbourg","Rome");

x.print(x.largestN(cities,2));

Console: [Saint-Petersbourg, San Francisco]

More

  • Invert dict: dict.invert();
  • Flatten list: list.flatten();
  • Modify list using slicing: trips.setAt(3, 5).values(x.list(1, 2, 3, 4, 5));
  • Case insensitive regex shortcut: x.RegexNoCase("\\bmama\\b")
  • Replace each match by an empty string: x.Regex("[,.;]").clear(inputString)
  • For more see the javadoc for the main class x.

Future

tuple match = x.String("I like apples.").longestMatch("My girlfriend likes apples too.");

x.print(match);

Console: ("apples", 7, 20)
  • Mistyping detection: x.String("Random").isMistypingOf("Randon","qwerty") --> true


xpresso

在python中使用Java编写代码

xpresso是一个灵感来自Python的Java库。它允许将(Python)代码线上重写为Java。这也是一个很好的方法,可以直接在Java中编写算法。

xpresso在Java熟悉的pythonic方法(例如len,enumerate,split / join,slicing)和编码范例(例如,一切都是可迭代的,列表推导,生成器,lambda表达式,使用谓词过滤迭代和使用函数进行修改) )。

xpresso还提供了多种有用的工具,通常是单线程,可以节省开发人员的时间,并使代码更易读:x.String,x.Object,x.Function,x.memo,x.WebService,x.MapReduce, x.go,x.timer,x.Json,x.mysql,x.csv等。

xpresso:更少的样板,更有趣,更多的工作。

许可证:[MIT]( https://en.wikipedia.org/wikei/MIT_License )。

用法

import com.wantedtech.common.xpresso.x;

x.print("Hello World!");

主要特点

Types similar to pythonic ones

import com.wantedtech.common.xpresso.types.*;

导入:set,dict,list,tuple,DefaultDict,OrderedDict,Bag,HappyFile,HappySQL

Slicable and iterable str type

str city = x.str("New York City");

x.print(city.slice(4,8));

Console: York

for (String character : city)
    x.print(character);

Console: N e w

Y o r k

One-line file open

Python:

with open("name.txt","r","utf-8") as f:
    #do stuff

xpresso:

try (HappyFile f = x.open("name.txt","r","utf-8")) {
    //do stuff
}

可以在文本和二进制模式下进行写入/读取/追加。

在Python中,以文本模式读取的文件是一个字符串:

Python:

for line in f:
    print line

xpresso:

for (String line : f)
    x.print(line);

Tuples

Python:

my_car = ("Honda", "red", 2010, True)

xpresso:

tuple myCar = x.tuple("Honda", "red", 2010, true);

动态名称赋值给元组元素:

myCar.name("make","color","year","good");
x.print(myCar.get("good"),myCar.get("make"),myCar.get("year"));

Console: true Honda 2010

如果名称方法尚未被调用,但是首次调用 get(someName),那么返回的值将是 get ),其中 i 是元组中剩余的未命名元素的最小索引。所有后续调用相同的值 someName ,相同的元素 i 将由 get(someName)返回。 您还可以定义和使用类型版本的元组。例如:

tuple3<String,String,Integer> myCar = x.tuple3("Honda", "red", 2010);

String myCarMake = myCar.left; String myCarColor = myCar.middle; Integer myCarYear = myCar.right;

tuple2<String,tuple3<String,String,Integer>> item = x.tuple2("car",myCar);

String type = item.key; //or, alternatively String type = item.left; tuple3<String,String,Integer> car = item.value; //or, alternatively tuple3<String,String,Integer> car = item.right;

Neat standard object creation

Python:

trips = ["Dubai","New York","London","Paris","Moscow","London","Saint-Petersburg","New York"]

russian_cities = set(["Moscow","Saint-Petersburg"])

rank = dict(("Moscow":30),("Saint-Petersburg":15),("New York":20),("London":10),("Paris":5),("Dubai":32))

xpresso:

list<String> trips = x.list("Dubai","New York","London","Paris","Moscow","London","Saint-Petersburg","New York");

set<String> russianCities = x.set("Moscow","Saint-Petersburg");

dict<Integer> rank = x.dict(x.tuple("Moscow",30),x.tuple("Saint-Petersburg",15),x.tuple("New York",20),x.tuple("London",10),x.tuple("Paris",5),x.tuple("Dubai",32));

Functions and predicates

import com.wantedtech.common.functional.*

Function<Object, String> toUpperCaseFun = new Function<Object, String>() { public String apply(Object value) { return value.toString().toUpperCase(); } };

list<String> tripsUp = x.map(toUpperCaseFun, trips); x.print(tripsUp);

Console: [DUBAI, NEW YORK, LONDON, PARIS, MOSCOW, LONDON, SAINT-PETERSBURG, NEW YORK]

Predicate<Object> containsO = new Predicate<Object>() {
    public Boolean apply(Object value) {
        return x.String("o").in(value.toString()) ? true : false;
    }
};

list<String> tripsO = x.filter(containsO, trips); x.print(tripsO);

Console: [New York, London, Moscow, London, New York]

每当你想以某种方式转换一个迭代时,你不需要定义一个新的Function类。您可以使用将任何Java类的任何静态方法自动包装到函数中的x.Function:

Function<String,String> myUpper = x.Function(String.class, "toUpperCase");

iterable = x.map(myUpper, iterable);

x.Function方法还可以包含带有几个参数的静态方法:

Function<tuple3<String,Integer,Boolean>,Double> mySomeFunc = x.Function(Some.class, "someStaticMethod");

Function<tuple3<String,Integer,Boolean>,Double> myOtherFunc = x.Function(Other.class, "someOtherMethod");

Function<tuple3<String,Integer,Boolean>,Double> funcToUse; if (someCondition) { funcToUse = mySomeFunc; } else { funcToUse = myOtherFunc; }

double sum; for (element : iterable) { sum += funcToUse.apply(x.tuple3(element,intParam,boolParam)); }

如果在某个类中有不止一个具有相同名称的静态方法,则需要通过提供参数类型来指定要包装的其中一个:

Function<tuple3<String,Integer,Boolean>,Double> mySomeFunc = x.Function(Some.class, "someStaticMethod", String.class, Integer.class, Boolean.class);

Lambda expressions

Python:

best_cities = reversed(sorted(item for item in rank.items(),lambda x: x[0]))

xpresso:

list<String> bestCities = x.reverse(x.sort(yield().forEach(rank.items()),x.lambdaF("x: x[0]")));

更复杂的lambda表达式:

Predicate<Object> pr = x.lambdaP("x : f0(f1(x[1])) == '''new york'''",x.lower,x.strip);
Function<Object,Integer> squareFun = x.lambdaF("x : x * x");

Function<Object,Integer> fun = x.lambdaF("x : x[0] * 10 * (x[1] - f0(x[2])))",squareFun);

功能链:

Function<Object,Integer> incrementFun = x.lambdaF("x : x + 1");
Function<Object,Integer> squareFun = x.lambdaF("x : x * x");

Function<Object,Integer> chainFun = x.chain(incrementFun,squareFun);

chainpun 将首先递增,然后对其输入进行平方。 x.chain(…)可以使用两个以上的函数作为参数。链中的最后一个函数必须返回所需输出类型的值。

List comprehensions

Python:

foreign_trips_lower = [city.lower() for city in trips if city not in russian_cities]

xpresso:

list<String> foreignTripsLower = x.list(x.<String>yield().apply(x.lower).forEach(trips).unless(x.in(russianCities)));

Python:

coolcities = dict([(city.upper(),true) for (city, score) in rank.items() if score > 5])

xpresso:

dict<Integer> coolCities = x.dict(x.yield("city","").apply(x.upper).replace(true).where("city","score").in(rank.items()).when(x.lambdaP("city, score : score > 20")));

Python:

evals = [True if value == "good" else False for value in some_list]

xpresso:

list<Boolean> evals = x.list(x.<Boolean>yield().replace(true).when(x.lambdaP("x : x == '''good'''")).replaceOtherwise(false).forEach(someList));

您可以使用列表推导从元素对象中提取属性:

class PlannedTrip {
    int year;
    String city;

<span class="pl-k">public</span> <span class="pl-en">PlannedTrip</span>(<span class="pl-k">int</span> <span class="pl-v">year</span>, <span class="pl-smi">String</span> <span class="pl-v">city</span>) {
    <span class="pl-c1">this</span><span class="pl-k">.</span>year <span class="pl-k">=</span> year;
    <span class="pl-c1">this</span><span class="pl-k">.</span>city <span class="pl-k">=</span> city;
}

<span class="pl-k">public</span> <span class="pl-k">int</span> <span class="pl-en">getYear</span>() { <span class="pl-k">return</span> year; }
<span class="pl-k">public</span> <span class="pl-smi">String</span> <span class="pl-en">getCity</span>() { <span class="pl-k">return</span> city; }

}

list<PlannedTrip> plans = x.list(new PlannedTrip(2015, "Moscow"), new PlannedTrip(2016, "Paris"));

list<tuple> plansData = x.list(x.yield("year", "city").where("year", "city").in(plans));

x.print(plansData);

Console: [(2015, Moscow), (2016, Paris)]

您也可以使用相同的表达式过滤提取的值:

list<tuple> plansData = x.list(x.yield("year", "city").where("year", "city").in(plans).when(x.lambdaP("year, city : year > 2015)));

x.print(plansData);

Console: [(2016, Paris)]

RESTful web services

我们假设我们有一个类SomeMath的对象,它有两种方法,我们想在网络上发布作为RESTful Web服务getSum和getProduct:

public class SomeMath() {
    public Double getSum(Double[] values) { //we want to publish this one
        return x.sum(values);
    }
    public Double getProduct(Double x, Double y) {//and this one
        return x  y;
    }
    public Double anotherMethod(Double somethingElse) {//but not this one
        return somethingElse;
    }
}

为了将我们的SomeMath类转换为Web服务,我们只需要首先用@ExposeAs注释来注释我们要从网络调用的两个方法,然后启动我们的Web服务:

public class SomeMath() {
    public Double getSum(@ExposeAs("values") Double[] values) {
        return x.sum(values);
    }
    public Double getProduct(@ExposeAs("x") Double x, @ExposeAs("y") Double y) {
        return x  y;
    }
    public Double anotherMethod(Double somethingElse) {
        return somethingElse;
    }
}

WebService ws = x.WebService(new SomeMath(), 8080).start();

这就是所有!我们的网络服务正在运行。我们来测试一下在浏览器中打开以下URL:

http://localhost:8080/SomeMath/getSum?values=5&values=6&values=7

输出:

18.0

现在打开以下URL:

http://localhost:8080/SomeMath/getProduct?x=5&y=10

输出:

50.0

如果一个方法返回一个更复杂的类的输出类型,例如Java的标准Map和List,或xpresso自己的list和dict,则输出将是相应的JSON字符串。

Generators

Python:

def firstn(n):
    num = 0
    while num < n:
        yield num
        num += 1

for i in firstn(500000): print i

xpresso:

public Generator<Integer> firstn (final int n) {
    return new Generator<Integer>() {
        public void generate() {
            int num = 0;
            while (num < n) {
                yield(num);
                num++;
            }
        }
    };
}

for (int i : firstn(500000)) x.print(i);

Memoization

作为一个简单的例子,让xerox 是一个应用 的函数对象复制字符串hello

的给定号码
Function<Integer, String> xerox = new Function<Integer, String>() {
    public String apply(Integer count) {
        return x.String("hello").times(count);
    }
};

为避免长时间计算相同的计数,我们首先使用 x.memo 创建一个缓存的xerox版本:

Function<Integer,String> cachedXerox = x.memo(xerox);

函数的第一个调用。计算需要很长时间:

x.timer.start();
String copies = cachedXerox.apply(5000000);
x.print(x.timer.stop());

Console: 18.898s

具有相同值的计数的第二个调用结果是即时的:

x.timer.start();
String moreCopies = cachedXerox.apply(5000000);
x.print(x.timer.stop());

Console: 0.0s

x.memo 可用于缓存任何Java类型对象的方法,而不仅仅是功能。请注意使用标准的x.timer :不需要创建额外的计时器对象。

Concurrency (beta)

xpresso中的并发是由 Go 启发的,因此非常简单。首先,将工作者定义为谓词的实例:

Predicate<Channel<Integer>> worker = new Predicate<Channel<Integer>>() {
    public Boolean apply(Channel<Integer> channel) {
        while (some_condition_true) {
            Integer value = computeValue();   //compute something in parallel
            channel.send(value);          //send the computed value to the channel
        }
        return true;                       //everything went as expected
    }
};

然后,一旦这些值准备就绪,就将通道定义到工作人员应该发送计算值的位置:

Channel<Integer> channel = x.Channel(Integer.class);//this channel only accepts Integer values

然后,根据需要启动尽可能多的并发工作人员

x.go(worker, channel);
x.go(worker, channel);
x.go(worker, channel);

最后,在需要这些值时,从通道中检索由工作人员同时计算的值:

for (Integer value : channel) {
    x.print(value);
}

MapReduce (beta)

我们假设我们有一个要处理的元素列表:

list<String> elements = x.list("Map","aNd","ReDuce","arE","aWEsome");

每个元素的处理需要很长时间(10秒),所以我们要并行处理我们的多核机器。让处理如下:如果元素以a开头,则将其放入大写,并使用〜作为分隔符与其他大写元素连接;如果元素不是以a开始,那么将其放入小写并将其与其他小写字相连。

我们定义Mapper和Reducer:

import com.wantedtech.common.xpresso.experimental.concurrency.Mapper;
import com.wantedtech.common.xpresso.experimental.concurrency.Reducer;

static Mapper<String,String> mapper = new Mapper<String,String>() { public void map(String input) { x.Time.sleep(10); //the processing of each element takes a long time :-) if (x.String(input).startsWith("a")) { yield("upper", input.toUpperCase());
} else { yield("lower", input.toLowerCase()); } } };

static Reducer<String,String> reducer = new Reducer<String,String>() { public void reduce(tuple2<String,list<String>> input) { yield(input.key,x.String("~").join(input.value)); } };

我们的映射器如上所述进行字符串大小写的转换,我们的reducer将结果值与〜结合。

我们的MapReduce设置现在已经准备好了,所以让我们开始练习:

x.timer.start();
x.print(x.<String,String,String>MapReduce(elements).map(mapper).reduce(reducer), x.timer.stop());

Console: {upper:AND~AWESOME~ARE, lower:reduce~map} 10.013s

正如你所看到的,所有5个元素的处理只需要10秒钟,而我们上面定义了每个元素的处理需要10秒钟。

JSON

记住等级 dict:

dict<Integer> rank = x.dict(x.tuple("Moscow",30),x.tuple("Saint-Petersburg",15),x.tuple("New York",20),x.tuple("London",10),x.tuple("Paris",5),x.tuple("Dubai",32));
我们先把它转储成String:

String rankAsString = x.Json(rank).toString();
x.print(rankAsString);

Console: {"New York":20,"London":10,"Saint-Petersburg":15,"Moscow":30,"Dubai":32,"Paris":5}

现在让我们从其JSON字符串表示形式创建一个等级的副本

dict<Integer> rankCopy = x.String(rankAsString).parseJson();

比较原始等级 dict到副本:

x.print(x.Object(rank).equals(rankCopy));

Console: true

CSV

从文件读取:

try (HappyFile f = x.open("filename.txt","r","utf-8")) {
    for (list<String> row : x.csv(f)) {
        //do stuff
    }
}

或者简单地:

list<list<String>> data = x.list(x.csv("filename.txt","r","utf-8"));

写入档案:

try (HappyFile f = x.open("filename.txt","w","utf-8")) {
    for (list<?> row : data){
        x.csv(f).writerow(row);
    }
}

或者简单地:

try (HappyFile f = x.open("filename.txt","w","utf-8")) {
    f.write(x.csv(data).toString());
}

写入一个StringBuilder:

StringBuilder builder = new StringBuilder();

for (list<?> row : data) { x.csv(builder).writerow(row); }

String cs = c.toString();

或者简单地:

String cs = x.csv(data).toString();

MySQL

String host = "host:port";
String user = "user";
String password = "password";
String db = "db";

try (HappySQL sql = x.mysql(host, user, password, db)) { try (HappySQL sql2 = x.mysql(sql)){ String query = "SELECT ID FROM " + "tbl_Employees e " + "WHERE e.Name LIKE ?"; for (tuple row : sql.execute(query, "John %")) { query = "UPDATE tbl_Employees " + "SET Promoted = 1 " + "WHERE ID = ?"; sql2.execute(query, row.get("ID")); } } }

Extended String functions

Python:

if "e" in "Hello World":
    #do stuff

xpresso:

if(x.String("e").in("Hello World")) {
    //do stuff
}

Python:

colorsPattern = "|".join(["black","green","red","white"]);

print colorsPattern

>>> black|green|red|white

xpresso:

String colorsPattern = x.String("|").join(x.list("black","green","red","white"));

x.print(colorsPattern);

Console: black|green|red|white

Python:

tokens = "Moscow;London;Paris".split(";")

print tokens

>>> ['Moscow', 'London', 'Paris']

xpresso:

list<String> tokens = x.String("Moscow;London;Paris").split(";");

x.print(tokens);

Console: [Moscow, London, Paris]

音译:

String trans = x.String("Чичётка 北亰").translit();

x.print(trans);

Console: Čičëtka bei jing

x.print(trans.stripAccents());

Console: Cicetka bei jing

将unicode转换为ascii:

String unidec = x.String("Чичётка 北亰").unidecode();

x.print(unidec);

Console: Chichiotka bei jing

大致的字符串比较:

x.print(x.String("Hello World").similarity("Hello Wold!"))

Console: 91

输出与 FuzzyWuzzy 完全兼容。

近似模式匹配:

x.print(x.String("You are cooding in Java.").search("coding"));

Console: 8

获取类似的字符串:

list<String> lookAlikes = x.String("apple").lookAlikes(x.list("ape", "apples", "peach", "puppy"),50);

x.print(lookAlikes);

Console: [ape, apples]

令牌:

String text = "English is hard. It can be understood through tough thorough thought, though.";

for (Sentence s : x.String.EN.tokenize(text)) { for (Token t : s) { x.print(t); } }

Console: English is hard . It can

Part-of-speech tagging

使用xpresso,您可以使用非常快速准确的(〜97%)Stanford CoreNLP english-left3words模型轻松地将POS标记为任何英文文本:

import com.wantedtech.common.xpresso.sentence.Sentence;
import com.wantedtech.common.xpresso.sentence.PosTagger;
import com.wantedtech.common.xpresso.sentence.pos.en.stanford.MaxentPosTagger;

PosTagger posTagger = new MaxentPosTagger(); String text = "Some English text. Multiple sentences."; for (Sentence sent : x.String.EN.tokenize(text)) { posTagger.tag(sent); x.print(sent.getAnnotations("pos")); }

Console: [(Some, DT), (English, NNP), (text, NN), (., .)] [(Multiple, JJ), (sentences, NNS), (., .)]

Slicing for list, String, and str

Python:

trips = ["Dubai","New York","London","Paris","Moscow","London","Saint-Petersburg","New York"]

print trips[2:4]

>>> ['London', 'Paris']

xpresso:

x.print(trips.slice(2,4));

Console: [London, Paris]

Python:

print trips[:5]

>>> ['Dubai','New York','London','Paris','Moscow']

xpresso:

x.print(trips.sliceTo(5));

Console: [Dubai, New York, London, Paris, Moscow]

支持负和非单位步骤:

Python:

print trips[::-1]

>>> ['New York', 'Saint-Petersburg', 'London', 'Moscow', 'Paris', 'London', 'New York', 'Dubai']

xpresso:

x.print(trips.slice(-1));

Console: [New York, Saint-Petersburg, London, Moscow, Paris, London, New York, Dubai]

Python:

print trips[::2]

>>> ['Dubai','London','Moscow','Saint-Petersburg']

xpresso:

x.print(trips.slice(2));

Console: [Dubai, London, Moscow, Saint-Petersburg]

Slicer object

Slicer LAST_THREE = x.sliceFrom(-3);

x.print(x.String("tic tac toe").slice(LAST_THREE));

Console: toe

Iterable regex search results

Python:

for long_word_match in re.finditer("\b\w{10,}\b",text):
    print long_word_match.group(0)

xpresso:

for (Match longWordMatch : x.Regex("\b\w{10,}\b").findIter(text))
    x.print(longWordMatch.group(0));

Python:

for long_word in re.findall("\b\w{10,}\b",text):
    print long_word

xpresso:

for (String longWord : x.Regex("\b\w{10,}\b").findAll(text))
    x.print(longWord);

Replace with a Function

Python:

def toUpperCaseFun(value):
    return value.group(0).upper()

text = re.sub("\b\w{10,}\b",toUpperCaseFun,text)

xpresso:

Function<Match,String> toUpperCaseFun = new Function<Match,String>(){
    public String apply(Match value) {
        return value.group(0).toUpperCase();
    }
}

text = x.Regex("\b\w{10,}\b").sub(toUpperCaseFun,text);

Replace with a dict

dict<String> replacer = x.dict(x.tuple("bad","good"),x.tuple("small","big"),x.tuple("hard","easy"));

text = x.Regex(replacer).sub(text);

Predefined regex patterns

list<String> emails = x.list(x.Regex.EMAIL.findAll("Contact me at john.smith@company.com or john@smith.com."));
x.print(emails);

Console: [smith@company.com, john@smith.com]

其他模式包括x.Regex.LINK,x.Regex.EMAIL,x.Regex.IPV4,x.Regex IPV6,x.Regex.HEX_COLOR,x.Regex.ACRONYM,x.Regex.CREDIT_CARD,x.Regex .FLOAT等等,还有一些国家/地区:x.Regex.US.DATE,x.Regex.US.TIME,x.Regex.US.PHONE,x.Regex.US.PRICE, x.Regex.US.STREET_ADDRESS等。

The Token type

Token tok = x.Token("MySQL5");
x.print(tok.shape(), tok.isCamel(), tok.hasDigits(), tok.hasRussian());

Console: ULUUUD, true, true, false

tok = x.Token("Thinking");
x.print(tok.stem());

Console: Think

hashCode(), equals(…), and compareTo(…) builders

定义类时

@Override
int hashCode() {
    return x.Object(this).hashCode();
}

在上面的代码中,xpresso首先通过反射来查找这个的成员,然后根据其成员的值来动态地计算这个的哈希码。

@Override
boolean equals(Object obj) {
    return x.Object(this).equals(obj);
}

在上面的代码中,xpresso首先查找两个对象的成员(这个 obj ),然后比较这些成员的值。

@Override
public int compareTo(Object obj) {
    return x.Object(this).compareTo(obj, fieldName0, fieldName1, );
}

在上面的代码中,xpresso首先查找两个对象的成员(这个 obj )。然后,如果这些成员的名称在输入字段名称fieldName0,fieldName1等中列出,那么将比较两个对象之间的成员的值。成员的值之间的比较顺序与输入字段名称的顺序相同。 / p>

Assertions

x.assertTrue(condition);    // throws IllegalArgumentException

x.assertNotNull(parameter); // throws NullPointerException

x.assertNotEmpty(iterable); / throws NullPointerException if iterable is null, throws IllegalArgumentException if iterable is empty / x.assertNotEmpty(string);

x.assertNotEmpty(array);

Built-in iterators

  • 循环
for (String letter : x.cycle(x.str("ABC")))
    x.print(letter);

Console: A B C A B C A B C

for (String letter : x.cycle(x.list("hello","world"),3))
    x.print(letter);

Console: hello world hello world hello world

  • 重复
for (String word : x.repeat("cool"))
    x.print(word);

Console: cool cool cool cool

for (String word : x.repeat("cool",3))
    x.print(word);

Console: cool cool cool

  • 计数
for (Integer index : x.countTo(3))
    x.print(index);

Console: 0 1 2

for (Integer index : x.countFrom(10))
    x.print(index);

Console: 10 11 12 13

for (Integer index : x.count(3,10))
    x.print(index);

Console: 3 4 5 6 7 8 9

for (Integer index : x.count(3,10,3))
    x.print(index);

Console: 3 6 9

x.count(min,max)和x.count(min,max,step)替换Python的范围(最小,最大)和范围(最小,最大,步长)。

Print anything

x.print("Hello World", 1, true, x.list(1, 2, 3), null);

Console: Hello World 1 true [1, 2, 3] NullType

N-grams

str phrase = "If you want something done right, you have to do it yourself.";

list<str> tokens = phrase.split();

list<list<str>> ngrams = tokens.ngrams(3);

x.print(ngrams);

Console: [[If, you, want], [you, want, something], [something, done, right], [do, it, yourself.]]

largestN and smallestN

list<String> cities = x.list("Moscow","San Francisco","Saint-Petersbourg","Rome");

x.print(x.smallestN(cities,2));

Console: [Rome, Moscow]

list<String> cities = x.list("Moscow","San Francisco","Saint-Petersbourg","Rome");

x.print(x.largestN(cities,2));

Console: [Saint-Petersbourg, San Francisco]

More

  • Invert dict: dict.invert();
  • Flatten list: list.flatten();
  • Modify list using slicing: trips.setAt(3, 5).values(x.list(1, 2, 3, 4, 5));
  • Case insensitive regex shortcut: x.RegexNoCase("\bmama\b")
  • Replace each match by an empty string: x.Regex("[,.;]").clear(inputString)
  • For more see the javadoc for the main class x.

Future

tuple match = x.String("I like apples.").longestMatch("My girlfriend likes apples too.");

x.print(match);

Console: ("apples", 7, 20)

  • Mistyping检测: x.String(Random)。isMistypingOf(Randon,qwerty) - &gt; true




相关问题推荐