Посмотрел я на приведенный JS код, на то как все в одну кучу смешано, как ошибки во входной строке не обрабатываются, как выражения не оптимизируются. И, в основном с целью с parser combinators поиграться, переписал на Scala. Мухи получились отдельно, котлеты отдельно, потом еще оптимизацию прикрутил.
2-ая задача (Scala)
import scala.util.parsing.combinator._
import scala.util.parsing.input.CharSequenceReader
sealed abstract trait Expr {
def allStrings: List[String]
def isLiteral = false
def isEmpty = false
}
case class Literal(s: String) extends Expr {
override def allStrings: List[String] = List(s)
override def isLiteral = true
}
case object Empty extends Expr {
override def allStrings: List[String] = Nil
override def isEmpty = true
}
case class Multi(es: List[Expr]) extends Expr {
override def allStrings: List[String] = es.flatMap(_.allStrings)
}
case class Concat(es: List[Expr]) extends Expr {
override def allStrings: List[String] = allStrings(List(""), es)
private def allStrings(prefixes: List[String], es: List[Expr]): List[String] = es match {
case Nil => prefixes
case h :: t => allStrings(for (p <- prefixes; s <- h.allStrings) yield p + s, t)
}
}
trait ExprParsers extends RegexParsers {
override val skipWhitespace = false
def literal: Parser[Literal] = """[^{|}]+""".r ^^ { Literal(_) }
def multi: Parser[Multi] = "{" ~> repsep(cexpr, "|") <~ "}" ^^ { l => Multi(l.distinct) }
def concat: Parser[Concat] = (mexpr +) ^^ { Concat(_) }
def cexpr: Parser[Expr] = concat
def mexpr: Parser[Expr] = literal | multi
}
trait ExprOptParsers extends ExprParsers {
override def cexpr: Parser[Expr] = super.cexpr ^^ { _ match {
case Concat(List(x)) => x
case Concat(l) if l.forall(_.isLiteral) => Literal(l.map(_ match {case Literal(t) => t}).mkString)
case Concat(l) if l.exists(_.isEmpty) => Empty
case x => x
}}
override def mexpr: Parser[Expr] = super.mexpr ^^ { _ match {
case Multi(Nil) => Empty
case Multi(List(x)) => x
case Multi(l) if l.exists(_.isEmpty) => Empty
case x => x
}}
}
object Parser extends ExprOptParsers {
private lazy val main: Parser[Expr] = phrase(cexpr)
private def parse(s: String): Expr = main(new CharSequenceReader(s)) match {
case Success(e, _) => e
case NoSuccess(m, n) => throw new IllegalArgumentException("Parsing error at " + n.pos + "\n" +
n.pos.longString + ": " + m)
}
def printAll(s: String): Unit = {
val p = parse(s)
println(p.allStrings.mkString("\n"))
println(p)
}
def shouldFail(s: String): Unit = {
try {
val p = parse(s)
} catch {
case e: IllegalArgumentException => println(e.getMessage)
}
}
def main(args: Array[String]): Unit = {
shouldFail("{")
shouldFail("}")
shouldFail("")
printAll("тест")
printAll("тест {тест}")
printAll("{тест} {тест}")
printAll("{тест {тест}}")
printAll("{}")
printAll("{{{{{{{{{{тест {тест}}}}}}}}}}}")
printAll("{A | B | C} тест")
printAll("{A | B} {A | B}")
printAll("{A |A | B} {A | B| B}")
printAll("{Пожалуйста|Просто} сделайте так, чтобы это {удивительное|крутое|простое} тестовое предложение {изменялось {быстро|мгновенно} случайным образом|менялось каждый раз}.")
printAll("{Пожалуйста|Просто} сделайте так, чтобы это {удивительное|крутое|простое} тестовое предложение {изменялось {быстро{}|мгновенно} случайным образом|менялось каждый раз}.")
}
}
Упомичая Тьюринга невозможно не вспомнить и Алонзо Чёрча. Его работы появились несколько ранее работ Тьюринга, а лямбда-исчисление все же более «удобный» формализм чем машина Тьюринга.
Перед тем как Хабр превратится в Трибуну УФН позволю себе процититовать:
«If I ever design a programming language, I will name it Neutrino, so I can claim it is faster than C.»
Есть такая фраза 'Past Performance Does Not Guarantee Future Results' и по моему сугубо личному мнению, на ворпос о прошлой работе тратить более 5 минут бесполезно. Во время интервъю надо выяснить принесет ли он пользу на новом месте (что знает, что умеет) и подходит ли он команде и компании.
Вопросы лучше подбирать без подвохов, такие где до простого решения можно додуматься самому. Ключевое слово — додуматься, как правило, не надо спрашивать то что требует очень узких знаний. Хорошо если вопрос можно углубить — это поможет отличить очень хорошего специалиста от просто хорошего.
Я всегда прошу в одном из вопросов писать код, т.к. к сожалению видел много людей которые либо уже разучились код писать и считают себя крутыми архитекторами либо еще не научились но считают себя крутыми хакерами.
Вопросы про разработку архитектуры приложения, системы или сервиса спрашиваю только достаточно толковых/опытных. С остальными это как правило выброшенное время, да и с более слабыми кандидатами на это редко хватает отведенного на интервъю времени.
А до этого чем компилировали, если Eclipse, то последний раз когда я его использовал там версия scalac была 2.7.7, в 0.3.312 — 2.8 пререлиз. 2.8 не полностью совместим с 2.7. Ну и пререлиз само собой подразумевает прекачество.
Если охота покапаться можно попробовать с командной строки 2.8 скомпилировать. Если неохота, и проект открытый могу и сам покопаться.
Субъективно стало лучше, например, по двойному ctrl+space видны недоступные методы. Специфически Scala плагин часто огрубляет дополнение при наличии ошибок. Видимо свой парсер не такой устойчивый/продвинутый как для Java, и пока scalac typer не вычислит все типы, предлагает только методы класса Object.
scalac когда компилирует занемяет пунктуацию в именах на мнемонику, = на $eq, + на $plus, — на $minus и т.д.
Например, если в Scala имеется класс
package test
class Test {
def test_=(s: String) = println(s)
}
То в Java к test_= методу можно доступится следующим образом:
package test;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
Method m = Class.forName("test.Test").getMethod("test_$eq", String.class);
m.invoke(new Test(), new Object[] {"scala"});
}
}
ну или если reflection не нужен то:
package test;
public class Main {
public static void main(String[] args) throws Exception {
new Test() {
public void callScala(String s) {
test_$eq(s);
}
}.callScala("java");
}
}
Я использовал все три, дольше всего Eclipse, для Netbeans плагин совсем не понравился — сыроват еще, но в последнее время пересел на IDEA. Для тех кому все равно какое IDE использовать посоветовал бы IDEA, но и в Eclipse поддержка Scala вполне достойная.
Даже если параметр используется, но только внутри метода, т.е. не присваивается полю класса и не передается другим методам в качестве параметра, память может быть освобождена.
Помните, каждая строка — неизменяемый объект.
Делая так: String x = «ads» + «dsada»; вы сжираете не 8*2 а 16*2 байт. Сначала происходит создание первых двух строк, затем третьей.
Строго говоря это неправда. Javac (компилятор Java) осуществляет т.н. свертку постоянных (constant folding).
String x = "ads" + "dsada"; эквивалентно String x = "adsdsada";
Чтобы избежать этого были придуманы StringBuffer и StringBuilder. Если вам нужно строить длинные строки — используйте эти штуки. Они быстрее и менее требовательны к памяти (так как не копируют строки), а StringBuffer ещё и потоко-безопасен (зато чуть больше тормозит).
Пример:
StringBuilder hi = new StringBuilder;
hi.append(«Привет, мир»);
hi.append(". ");
hi.append(«Как твои дела?»);
System.out.println(hi.toString());
Во-первых append копирует аргумент а toString создает копию строки.
Во-вторых приведенный код менее эффективен чем System.out.println(«Привет, мир» + ". " + «Как твои дела?»);
Ну а в-третьих, даже если производится конкатенация переменных строк а не постоянных, вместо:
Я когда начинал с XSLT работать учился по "XSLT: Programmer's Reference by Michael Kay". Для тех кто с версией 1.0 работает наверно до сих пор ничего лучше нет. Версия 2.0 имхо лучше всего описана у него же в других двух книгах: XSLT 2.0 Programmer's Reference & XPath 2.0 Programmer's Reference.
«If I ever design a programming language, I will name it Neutrino, so I can claim it is faster than C.»
Вопросы лучше подбирать без подвохов, такие где до простого решения можно додуматься самому. Ключевое слово — додуматься, как правило, не надо спрашивать то что требует очень узких знаний. Хорошо если вопрос можно углубить — это поможет отличить очень хорошего специалиста от просто хорошего.
Я всегда прошу в одном из вопросов писать код, т.к. к сожалению видел много людей которые либо уже разучились код писать и считают себя крутыми архитекторами либо еще не научились но считают себя крутыми хакерами.
Вопросы про разработку архитектуры приложения, системы или сервиса спрашиваю только достаточно толковых/опытных. С остальными это как правило выброшенное время, да и с более слабыми кандидатами на это редко хватает отведенного на интервъю времени.
Если охота покапаться можно попробовать с командной строки 2.8 скомпилировать. Если неохота, и проект открытый могу и сам покопаться.
Ну а вообще YMMV как говорится :),
Например, если в Scala имеется класс
То в Java к test_= методу можно доступится следующим образом:
ну или если reflection не нужен то:
Или Scala for Netbeans или как уже упоминали Eclipse с плагином.
Я использовал все три, дольше всего Eclipse, для Netbeans плагин совсем не понравился — сыроват еще, но в последнее время пересел на IDEA. Для тех кому все равно какое IDE использовать посоветовал бы IDEA, но и в Eclipse поддержка Scala вполне достойная.
Заранее спасибо.
Строго говоря это неправда. Javac (компилятор Java) осуществляет т.н. свертку постоянных (constant folding).
Во-первых append копирует аргумент а toString создает копию строки.
Во-вторых приведенный код менее эффективен чем System.out.println(«Привет, мир» + ". " + «Как твои дела?»);
Ну а в-третьих, даже если производится конкатенация переменных строк а не постоянных, вместо:
javac генерирует байт-код эквивалентный:
Так что не бойтесь использовать + со строками.
Но решавшими не учтен релативистский еффект! всем думать еше 2 часа