Как стать автором
Обновить

Комментарии 83

Ну зачем вы меня на ночь так расстроили?
Чем же?
Не ясно только, зачем для этого слезать с C#

Enumerable.Range(2, (int)Math.Sqrt(N)).Any(n => N % n == 0)

Ну и, соответственно

Enumerable.Range(2, (int)Math.Sqrt(N)).AsParallel().Any(n => N % n == 0)
Согласен, подправил заголовок.
Не совсем так. Скала, насколько я помню, свои range хранит как объект и при par вполне может очень эффективно этот объект разбить, не итерируясь по всему range. Донетовский Range, считай, простой набор yield return.
А какая практическая разница, учитывая, что для решения задачи все равно надо проитерировать?

Впрочем, в LINQ есть ParallelEnumerable.Range.
Из чистого занудства можно еще вспомнить. что сумму по Range, contains, min, max, length, take, drop и пару других операций в scala можно получить за O(1). Отличие не большое, но есть.
Богато.

Кстати, что занятно, ничего не мешает достичь того же эффекта в .net, причем прозрачно для программистов.
Достаточно прозрачно для меня: source.

Честно говоря мне сложно представить как в LINQ сделать подобное за O(1) прозрачно:
IEnumerable<int> r = ParallelEnumerable.Range(start, count); //Потеряна информация о типе, например передали как параметр
var r2 = r.Skip(i);

Проверка конкретного типа this в Enumerable.Skip — не прозрачно.

PS: повторюсь, что преимущество явно минорное.
Если информация о типе потеряна, то никак (если не вводить интерфейс «оптимизированных вычислений» для подобных вещей).
На 30000 итераций (поиск простых N порядка 10^9) ParallelEnumerable.Range на 8-ядерном процессоре только-только догоняет обычный for. Enumerable.Range(...)).AsParallel() проигрывает им примерно 8 раз.
Если взять N порядка 10^7 (до 3000 итераций), то ParallelEnumerable.Range медленнее, чем for, в 6 раз, а Enumerable.Range(...)).AsParallel() — в 25 раз. Существует ли число итераций, на котором Enumerable.Range(...)).AsParallel().Any() даст хоть какой-нибудь выигрыш по сравнению с Enumerable.Range(...)).Any(), я не уверен (10^8 итераций — перебор делителей не до sqrt(N), а до N-1 — не хватило, AsParallel там проигрывает 2 раза).
Код плохочитабельный. Хотя в отношении scale, возможно просто с непривычки (местами даже вполне симпатичный), но приведенный пример кода на C# просто ужасен. Листая код, я бы завис над такой строчкой секунд на 20 точно, а может того и больше…
Вы до сих пор живете без лямбд?
Вы знаете, многие до сих пор живут без них…
Такое же впечатление после прочтения статьи. Автор зачем-то изменил убедительный пример из книги Programming in Scala:
// this is Java
boolean nameHasUpperCase = false;
for (int i = 0; i < name.length(); ++i) {
  if (Character.isUpperCase(name.charAt(i))) {
    nameHasUpperCase = true;
    break;
  }
}
// Whereas in Scala, you could write this:
val nameHasUpperCase = name.exists(_.isUpper)
Простите, а нафига так писать?

вариант 1:
public static boolean haveUpperCase(String name) {
   for(char ch: name.toCharArray()) {
       if (Character.isUpperCase(ch))
           return true;
   }
   return false;
}

вариант 2:
public static boolean haveUpperCase(String name) {
   return !name.toLowerCase().equals(name);
}

вариант 3:
public static boolean haveUpperCase(String name) {
   return name.matches(".*[A-Z].*");
}
Вариант 1 создает лишний массив.

Вариант 2 усложняет логику. Если закрыть название, то надо сделать несколько лишних мыследвижений для того, чтобы понять, что делает функция.

Вариант 3 не пройдет тест «Вася».
2. Сколько лишних мыследвижений надо сделать для того, чтобы понять, что в записи
val nameHasUpperCase = name.exists(_.isUpper)
— значит метод exitsts?
— откуда берётся _ и что значит?
Понятно, что к языку можно привыкнуть, но чем оно лучше-то?

3. Сильно зависит от задачи — регу можно и проще задать и точнее (но она будет сложнее).

Ну и в любом случае — пример глубоко синтетический (как часто вам нужно узнать такое?), а потому пользы не очень много несёт.
2. exists и _ — это такие же части языка Scala, как while и toString — Java. Вы же не задумываетесь о значение последних?

3. Зачем писать огромную регу, которую уж точно не каждый сразу поймет, когда можно достичь тогоже результата простым кодом? Чтобы схлопотать RuntimeException лишний раз?

В моем понимании, пример показывает общую тенденцию вполне коректно.
Извините, while и toString — написаны человеческим языком и ставятся так, что особых вопросов не возникает.

В то же время _ — вообще непонятно что собой представляет, а exists меня крайне смущает — оно перебирает массив симолов строки с выполнением условия или что? Ну и из названия я это действие никак просчитать не могу. Это — bad-style. К этому можно привыкнуть (как и к perl), но комфорта это не вызывает.

3. Затем, что задача может быть крайне сбоку для основного алгоритма. Для этого и нужен _пример_из_жизни_, а не синтетический пример. На синтетике можно придумать много совершенно различных путей решения, в реальном примере обстоятельства вынуждают сужать спектр решений.
Вы ссылаетесь на то, что вам не понятен этот код. Не-Java програмистам тоже Java код может показатся странным и непонятным.
Претензии к exists не обоснованы. У меня довольно большой опыт использования LINQ — все методы запоминаются очень быстро.
Интерфейс коллекций в scala запоминается так же быстро.
exists — метод над коллекцией, проверяющий есть ли в ней элемент, удовлетворяющий условию.
В LINQ — Any.
Мне сложно осознать что тут может быть не очевидного.

"_" — придираться к "_" — то же самое, что придираться к "." в java. Используется даже чаще ("." можно опускать во многих случаях).
А теперь скажите пожалуйста: в scala строка является просто массивом символов (по типу c-шного char*)? Если нет, тогда это не самая очевидная (хотя и оправданная) надстройка, при этом являющаяся в чистом виде семантическим сахаром, а не реальной языковой особенностью. В противном случае случае разговор опять же не о том — сравнивать обработку объекта и массива некорректно.

Конечно java-метод contains не сравнится по функционалу с exists, но это не особенность подхода данного языка, это особенность встроенных инструментов (синтаксический сахар).

А вот сравнивать _ и. не совсем корректно — _ обозначает какой-то объект (при этом никаких отсылок к объекту, почему бы вместо _ не писать element?), а. — операцию доступа к свойствам объекта. Не самый очевидный способ, но касается иных материй (в том же примере scala точно так же использует этот способ).

PS Даёшь адекватные примеры в студию. Не надо синтетики!!!
1. У строки нет метода exists. Так же как в C# у коллекций нет метода Any.
В scala строку можно рассматривать как коллекцию Char. Это обеспечивается механизмом, являющимся одной из ключевых особенностей языка.

2. Как вы различаете особенности языка и особенности инструментов языка?

3. записи "_.toString" и «element => element.toString» для scala эквивалентны.
Еще можно писать new Function1[ElementType, String] { def apply(element: ElementType): String = element.toString() }, но я думаю вы понимаете почему так не пишут.

Не синтетика — код из реальных примеров?
Я вам обеспечу на этих выходных в виде статьи — договорились?
Куда же девался метод Any для коллекций в C#?
Вот такой код нормально компилируется:
            string s="123";
            ICollection<char> ss=new char[]{'4','5','6'};

            s.Any(c => Char.IsUpper(c));
            ss.Any(c => Char.IsUpper(c));
Да, компилируется. Но его нет.
Ибо на самом деле:

Enumerable.Any(s, c => Char.IsUpper(c));
Enumerable.Any(ss, c => Char.IsUpper(c));

There is no spoon. It's just an extension spoon.
строго говоря, у интерфейса ICollection нет метода Any. Это метод класса Enumerable.
1. Так синтаксический сахар же! :-)

2. Есть синтаксический сахар и есть парадигма, в рамках которой работает язык. Синтаксический сахар — дело наживное (например недавно появился в java switch по строкам, catch по нескольким типам исключений, try на потоках, которые надо закрывать и т.д.).
Конечно можно говорить, что в «языке а уже есть f, а в языке б — нету», но это не совсем то.

3. Потому и не пишут, что синтаксический сахар :). В java почти тем же способом являются вложенные классы, правда интерфейс должен быть объявлен заранее, да.

Замечательно! Реальные примеры рулят, только они могут показать — есть ли смысл и что даёт! Только одна небольшая просьба — ссылочку потом в личку скиньте или ещё как-либо маякните, я на выходных не всегда смотрю новые статьи… а потом могу и не найти. Заранее спасибо.
1, 2: Вы явно не знаете scala.
Это именно то, что вы назвали парадигмой.
Расширяемость языка. Я не зря привел в пример Any из C#.
В scala есть механизм ( implicit conversions), позволяющий приводить объекты к нужным нам интерфейсам. Если вы будете работать с объектом сторонней библиотеки и захотите использовать его как коллекцию, то вы сможете воспользоваться тем же механизмом.

Этот механизм не ограничен коллекциями и строками. Этот механизм не ограничен предустановленным набором преобразований. Этот механизм полностью управляем, его можно отключить, им постоянно пользуются.

3. Так все, кроме байткода — синтаксический сахар =)

Но я не обещаю примера именно работы с коллекциями. Сейчас просто пишу кое-что, достойное, надеюсь, упоминания.
1. А я где-то утверждал обратное? Мои претензии только к способу сравнения языков.
:-)

Т.е. любой объект можно привести к любому интерфейсу? Бред же, одно дело, если интерфейс поддерживается, другое — если нет. Так ведь и в java можно использовать приведение, правда явно указывая оное, чем хуже? Или вы про что-то другое?

3. Не-не-не! ООП != байткод, функциональщина != байткод и т.д. Грубо говоря есть доп функционал (switch), а есть базовый (class, уровни доступа). Но функциональщину не люблю — никак не могу простить Lisp'у поедание моего мозга… Хотя тот же C с полпинка начал понимать.

А мне не важно с чем работа будет — важен именно реальный пример, который можно сравнить с реальным примером другого языка (если на втором языке вообще получится) и тогда уже можно будет сравнивать языки. А синтетические примеры на 5-50 строк никуда не годятся.
1. Не привести (тут я не правильно выразился), а обернуть. Естественно надо описать преобразование или импортировать. Тут с 2.10 улучшили и обертка в большинстве функций на самом деле не создается.

3. Посмотрите ФП на примере scala — мозг не ест, сам проверял.
ФП (в скала) — сахар над ООП.
Реализация ООП в языках — структуры (с наследованием) + области видимости + автоматическая обработка виртуальных методов. Все это на чистом C вполне можно делать, только по таблицам виртуальных методов вручную пройтись придется, что не сложно.

Неявные преобразования и неявные параметры входят в идеологию scala и используются почаще switch. Уберите из scala неявные преобразования и параметры и получите Kotlin.
На этих механизмах в scala строятся вычисления на уровне системы типов (см shapeless).
А мне не важно с чем работа будет — важен именно реальный пример, который можно сравнить с реальным примером другого языка

А у меня идея!

У меня к статье уже готовы 2 небольших куска кода на scala: один на ~100, другой на ~250 строк. Это уже рабочий и оттестированный функционал.

Предлагаю эксперимент: вы перепишите этот функционал (или напишите аналогичный) и сравним.
Можно и так, только нужно чёткое ТЗ, чтобы не разминуться в действиях :-)
И много же воды с тех пор утекло.
Я успел забыть про статью, вспомнить и дописать (судя по комментариям — не слишком занимательное чтиво).

Вот состояние репозитория на момент нашего разговора: ссылка.
Там всего-навсего объектная модель для XPath v 1.0, парсер, соответствующий спецификации, и тесты на парсер.
Не используется ни одна из существующих реализаций, так как в последствии предполагается внести пару дополнительных элементов, не совместимых с существующими реализациями (см. статью).

А вообще да, шутка затянулась.
Предлагать реализовать парсер на чистой Java — издевательство.
2. Язык надо понять один раз, а вот подобные записи со временем понятнее не становятся.
Кстати ответ номер 2 не корректен по той же причине, по которой name.toLowerCase().toUpperCase().equals(name) вполне может дать false. (Зависит от языка).

3. «Рега» на все языки будет абсолютно не читаема.

Пример очень жизненный, просто надо смотреть шире.
Мне в LINQ очень часто требуется запись в духе es.Any(e => !e.Prop).

PS: не обновил комментарии, но оставлю.
Прошу прощения
подразумевалось:
«name.toLowerCase().toUpperCase().equals(name.toUpperCase) вполне может дать false»
2. А зачем вам лишние сущности с toUpperCase()? А без них работает замечательно (можете пример привести, где сломается?), читабельность не теряется, понять смысл можно по названию функции (очень быстро) и по действиям (чуть медленнее).

3. Зачем на все языки? Не верится в реальность такого ТЗ. А в некоторых обстоятельствах — вполне сгодится. Это не лучшее решение, это решение позволяющее сэкономить строки (ради чего и использовали scala).

Вот и надо реальные примеры приводить, где бы это хорошо юзалось.
2. Примеры приводят в каждой статье с названием в духе «почему вы используете юникод не правильно». Среди них бывают весьма познавательные.
Первое, что нагуглил:
In standard German orthography, the sharp s ("ß") is uppercased to a sequence of two capital S characters.

То есть для toUpperCase это сломается ("ß" != «ss»). Вполне может быть пример, когда сломается для toLowerCase. Лично я не готов гарантировать, что loLowerCase не сломает какую-нибудь лигатуру нижнего регистра.

3. Я очень сильно матерился, добавляя поддержку еще одного языка в систему, при разработке которой думали, что кроме русского и английского языков не бывает. Чтобы просто добавить возможность перевести интерфейс на третий язык потребовалось набить систему костылями, что сильно усложноло поддержку.
Мое мнение с тех пор: не ограничивайте область применения без причины.
Есть задача проверить на вхождение букв в верхнем регистре — проверияем на вхождение букв верхнем регистре, а не на то, что после приведение к нижнему регистру строка не изменится и тем более не каким-то нечитаемым регекспом.
Тогда надо было бы UpperCase и LowerCase поменять местами. А так получается
UpperCase("ß")=«SS»
UpperCase(LowerCase("ß"))=UpperCase("ß")=«SS»
И где противоречие?

Собственно,
"ß".ToUpperCase().equals("ß")==false
"ß".ToLowerCase().equals("ß")==true

"ß" ведет себя как маленькая буква… Или не так?
Я лишь указал корень проблемы: бывают и заглавные лигатуры.
Я не уверен сломает ли строчную лигатуру toLowerCase — мне придется над этом задуматься при чтении кода.
В данном случае (x.ToLowerCase().equals(x)) проблемы не видно: если буква заглавная (неважно, одиночная она или лигатура), она превратится в одну или несколько строчных — и строка изменится. Если буква строчная (опять же, может быть и одиночной, и лигатурой) — ей меняться, вроде бы, незачем. В обоих случаях приём работает. Единственная неприятная ситуация, которую я вижу — наличие буквы, которая является одновременно строчной и заглавной, и при операциях ToLowerCase и ToUpperCase переходит сама в себя.
Но задуматься над этим придётся. Получается такая оптимизация неизвестно чего (со всеми отрицательными эффектами оптимизации и без видимых положительных).
Для варианта 3 есть замечательные Unicode Character Properties регексы. "\p{Lu}*" для uppercase letter, например.
Если использовать guava, то можно также обойтить содной строчкой:

CharMatcher.JAVA_UPPER_CASE.matchesAllOf(name)
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Представте, что вы архитектор и занимаетесь планировкой городов, многоуровневыми дорожными развязками и сверхсовременными хабами аэропортов. Но стоит мне только показать вам как я ловко, голыми руками, набрасываю штукатурку на сетку рабица, я уверен, вы в тот же час бросите свой скучный офис с видом на океан, компанию со столетней историей, сожгёте свои банковские карточки и начнёте со мной постигать таинство того как вместо ста тридцати движений рукой сделать сто двадцать девять, но с тем же результатом!
Ну, есть инструменты для планирования городов, есть для штукатурки. Зачем их смешивать. Движений не 129 а значительно меньше. И за счет штукатурки- дома можно сторить повыше. А бросать проектирование при этом я не призываю. Или Вы полагаете — в Scala оно чем-то хуже?
Не знаю. Это и было бы интересным. В частности, на платформе .net кроме C# есть F# — функциональный язык. Кроме прямого использования линкю с лямдами в C# которых хватает за глаза, в том числе и для паралельных запросов к колекциям, можно написать модуль на F# и вставить его в свою программу при необходимости. .net обеспечит соответствие типов и всё остальное. Но у меня пока таких задач не попадалось.
Sсala работает на JVM, совместимо с java, так что все инструменты проектирования java -библиотеки, фраймворки- остануться.
+ появится много новых! akka, scalaz поглядите список — www.scala-lang.org/node/1209#libraries
Сравнение с С# интересно, но тут я некомпетентен.
Неубедительно. Ничто не мешает мне повысить уровень абстракции путем написания нужного кода и сокрытия его за красивыми методами. Плюс есть ряд библиотек, которые уже позволяют это сделать. Java 8 опять же облегчит труд нашим пальцам.

Ничего не имею против Scala. Просто неубедительно.

Ваш цикл на Java будет всегда выполнять ровно итерацию. Это делает статью еще менее убедительной, а оптимизацию по sqrt(N) вместе с комментарием и вовсе бессмысленными.
Спокойствие, человек находится под «Эффектом Scala» это проходит со временем, возрастом и количеством решенных практических задач. :)
Ну да. А еще там тем main(String arg[]) — вообще не исполнится.
Подправил опечатку. Надеюсь смысл был ясен и с ней.
Вот вы сравниваете уровни абстракции Scala и Java и при этом в качестве примера от лагеря Java приводите код, который точно так же выглядит, скажем, на C(даже не ++). Думаю, и лет 30 назад он выглядел точно так же. На мой взгляд — это элементарнейшая подмена понятий. Возьмите пример, задействующий реальные возможности Java, а не только название. Впрочем, далеко ходить не надо, тут чуть выше уже привели адекватный задаче пример кода на C#.
Приведите мне пример с реальными возможностями Java. C# не знаю:( Поэтому ошибся и исправил.
Тогда уж и С++ вычеркивайте. в нем тоже есть лямбды, фичи типа Accelerated Massive Parallelism и возможность добавить синтаксический сахар по вкусу.
Ну вот. Уже у всех кроме java есть. В 8 будет, но когда она будет. Поправил.
Если Вы считаете эти примеры кода крутыми — обратите внимание на .NET LINQ — не пожалете.
Или на Guava в Java.
Помню, тоже очень радовался, когда со спектрума на денди пересел.
В ФП абстракции не другого уровня. Они просто другие. Прежде всего в голове у программиста, а не в языке. Грубо говоря на обычном Си можно писать хоть в процедурном стиле, хоть в ФП, хоть в ООП.
А ООП вообще имеет отношение к паре «процедурный стиль — ФП», или независим от неё? Не может ли быть ООП и ФП одновременно (ну и что, что все объекты будут неизменяемы. Зато они сами будут разбираться, кого и как им создавать).
Ортогональны. Scala это и ООП, и ФП. Просто хотел показать что одного ООП — не хватает для выразительности.
Хороший вопрос. Думаю всё же имеет. Всё же классическое ООП подразумевает, что объекты имеют изменяемое сообщениями/методами состояние. Хотя, конечно, можно ограничить методы только чистыми функциями, забыть о синглтонах и реестрах и вообще ссылках на объекты и т. п. Но что-то мне кажется, что не очень продуктивно это будет, если полностью отказаться от императивных практик.
Чем мешают ссылки на объекты? Когда объекты станут неизменяемыми, ссылки будут работать еще лучше. Хотя вариант «объект изменился, а тот, кто на него ссылается, нет» — уже не пройдёт.
Изменяются ли монады в Хаскеле?
Монада изменятся не может по определению. Как может изменяться моноид? (Монада это моноид на категории эндофункторов). А моноид это совокупность множества операции над элементами множества и единичным элементом для выбранной операции. Например для натуральных чисел может быть такой моноид — натуральные числа, сложение, число 0. В некоторых монадах (IO, State) комбинируемые функции устроены таким образом что каждая из них принимает и возвращает объект специального типа, который сам по себе не изменяемый но может каждый раз возвращаться новый.
На счет того что при неизменяемых объектах ссылки работают намного лучше вы абсолютно правы. Кстати это пришло умным людям в голову много лет назад, отсюда и термины referential transparency, и целый класс структур данных, называемых — функциональными.
В конце марта на coursera.org предвидится интереснейший курс от Одерски, посвященный именно сабжу.
Спасибо, записался.
Курс делал в сентябре — сдал на 100%. Теперь про возможности только Scala =D
А при чем тут вообще ООП? Пока что вы пнули исключительно императивное программирование.

Ну и да, неумение отличить коллекцию от последовательности, конечно, тоже весьма забавляет.
Давать ссылку на торрент платной книги — не хорошо. Дайте на магазин. Кто захочет спиратить — сам нагуглит торрент.

Первая версия (со старыми коллекциями) доступна официально бесплатно.

А подробнейшее описание работы с современными коллекциями есть на сайте скалы: тыц.

И в книге наверняка нет многих других вкусностей, доступных на сайте:

Магазин дать — скажут реклама. Опять же — какой из них. Вообще убрал.
Не, а давайте я на scala напишу цикл, а на java — применю библиотеку с параллельными коллекциями — это будет честно?
цикла на scala писать не надо, а на java — было бы интересно посмотреть работу с библиотекой параллельных коллекций для сравнения.
Вот например extra166y

В конечном счете, все отличие scala от java сведётся к тому, что в java (пока, до java8) надо самому написать обертку для метода, чтобы передать его параметром, а в scala за вас это сделает компилятор. Может, вы найдете в scala более впечатляющие преимущества?
Так преимущество scala именно в декларативности и лаконичности.

Можно использовать библиотеки коллекций, библиотеки для многопоточности, везде писать final, генерировать геттеры и сеттеры при помощи IDE и многое другое. При этом вы приблизитесь к scala довольно близко — кода будет раза в 1,5 — 2 больше.

Добиться в java при помощи библиотек и несколько большего количества кода поведения, как в scala нельзя только в 2 моментах:

1. Гарантия оптимизации хвостовой рекурсии. Но тут можно развернуть рекурсию вручную.
2. Сопоставление с образцом. Но тут можно генерировать кучу «одноразовых» классов и писать кучу условий в явном виде.
Преимуществ много. Сюда например загляните stackoverflow.com/questions/2952732/samples-of-scala-and-java-code-where-scala-code-looks-simpler-has-fewer-lines
И сюда: www.scala-lang.org/node/27499
А вот работа с JSON:
github.com/playframework/Play20/wiki/ScalaJson
github.com/playframework/Play20/wiki/ScalaJsonCombinators

Библиотека интересная, для включения в java 8, как я понимаю.
И как на ней будет выглядеть !(2 to math.sqrt(N).toInt).par.exists(N % _ == 0)?
Библиотека с параллельными коллекциями на java — которая?

В принципе да, имея лямбды из java 1.8 и одну из сторонних библиотек параллельных коллекций, можно получить запись не на много длиннее, чем в scala.
Главное чтобы соответствующие методы в выбранной библиотеке принимали «функциональные интерфейсы».

Одна проблема: в java явная нехватка интерфейсов неизменяемых коллекций: даже Collection имеет метод Add.

Так что любая работа с «родной» коллекцией java при помощи сторонней библиотеки с параллельной обработкой приведет к полному копированию коллекции, что не приятно.
В scala же неизменяемые индексированные коллекции превращаются в параллельные без копирования.

Scala одними лямбдами не ограничивается. Есть множество других преимуществ.
Боже, умоляю Вас, перестаньте зачеркивать слова.
Сам очень люблю Scala, но примеры, которые приводит автор не убедительны.
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории