LINUX.ORG.RU
Ответ на: комментарий от nezamudich

Хорошо. Как тогда сравнить выразительность самих языков, а не высокоуровневых компонент их стандартных библиотек? Чтобы, грубо говоря, нельзя было сделать std::solve_this_particular_example_task().

Или ты считаешь, что использовать words и unwords в примере выше — это ок?

intelfx ★★★★★
()
Последнее исправление: intelfx (всего исправлений: 3)
Ответ на: комментарий от intelfx

Ну ок, там выше приводили вариант через unfoldr, он норм? Там используются ровно те же примитивы что и в поюсовом варианте. Кстати, я не совсем понял, что с переносами строк, просто программы (если я правильно плюсы распарсил) дадут разный вывод, ну и плюсы дампят все в stdout, а Haskell возвращает строку полученную что тоже не эквивалентно.

qnikst ★★★★★
()
Ответ на: комментарий от somequest

последовательность генерируемая функцией вполне может иметь длину.

var noLengthHere = function* (n) {
  while(true) {
    yield n++;
  }
}

В нормальных языках это можно zip'ать, filter'ить, map'ать и так далее.

Вообще-то в js есть итерабельные объекты, и Array, например, это итерабельный объект. причем тут map и filter?

Чуть выше — итерабельный объект (даже в JS он итерабелен).

x3al ★★★★★
()
Ответ на: комментарий от intelfx

А я не знаю, как правильно сравнивать выразительность. У меня на этот счёт про плюсы и хаскель есть лишь субъективное мнение (очевидное). Как провести нормальное объективное сравнение — не знаю.

Про words/unwords/lines/unlines могу лишь сказать, что в контектсе хаскеля это нужный «примитив». Особенно полезны при написании наколеночных парсеров для простых входных данных, которые нужны очень часто.

Почему их нет в плюсах — не знаю, может просто не декомпозируются так удачно?

nezamudich ★★
()
Ответ на: комментарий от monk

В «нормальном ООП» C++

Справедливости ради, ООП в данном решении только в виде класса std::string. Все остальное — это обобщенное программирование, которое в C++ сделали по мотивам ML (если мне склероз не изменяет).

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от nezamudich

Почему их нет в плюсах — не знаю, может просто не декомпозируются так удачно?

В Boost-е есть split для строк. Но в стандартной библиотеке нет. Впрочем, в C++ (даже в C++11/14) стандартная библиотека более чем скудная.

eao197 ★★★★★
()
Ответ на: комментарий от monk

Кстати, твой пример - это синтетика. Тут просто совпало так, что на этот случай есть хаскелевский сахарок — words lines и прочее говно. Если строку сплитить по другому, хаскель сольет почти всем, даже, возможно плюсам.

И при всем при этом, этот пример не особо впечатляет. Там, на той же самой странице, есть примеры на ООП языках, которые без сахара делают это почти так же лаконично, например JS. Если переписать этот код на Io или перл, будет еще раза в полтора-два короче.

somequest
()
Ответ на: комментарий от x3al

это можно zip'ать, filter'ить, map'ать и так далее.

Нельзя, так как это недетерминированно. Там, возможно сахар, для сохраненного результата итерации. Приведи пример, и я тебе перепишу его на JS.

somequest
()
Ответ на: комментарий от monk

А в страшном и непонятном Haskell:

Вот тебе для сравнения, в нестрашном и понятном Io

str split("\n") map (split(" ") reverse join(" ")) join("\n")

somequest
()
Ответ на: комментарий от somequest
var mapOverIterable = function* (func, iterable) {
  for (val of iterable) {
    yield func(val);
  }
};
squares = mapOverIterable(x => x * x, infinitGenerator());

Ничерта не вижу проблем. А методом — ну нафиг, оно уже умеет в iterable protocol, я недостаточно рубист для того, чтобы множить их без причины.

x3al ★★★★★
()
Последнее исправление: x3al (всего исправлений: 1)
Ответ на: комментарий от x3al

Не, это у тебя другая фигня, может быть форич ее можно назвать с натяжечкой. У map другая семантика. Он обязан вернуть конечный объект того же типа, что и обрабатываемый, с результирующими значениями.

somequest
()
Ответ на: комментарий от somequest

Обычный ленивый вариант map.

обязан вернуть конечный объект того же типа, что и обрабатываемый

Да ладно? А если туда arguments засунуть — он такой же недомассив вернёт?

x3al ★★★★★
()
Ответ на: комментарий от x3al

Обычный ленивый вариант map.

Может у вас пистонах подобную клоунаду так и называют, но это ничего общего с map не имеет.

А если туда arguments засунуть — он такой же недомассив вернёт?

Тут утиная типизация. Если для обеих объектов достаточно свойств, чтобы считать их эквивалентными для применения, ок. тип «массив» - это *концептуально* подтип «недомассив». В этом случае, объекты в контексте применения следует считать однотипными. Обычная практика в ООП.

somequest
()
Ответ на: комментарий от somequest

Оно во многих языках ленивое. Без ленивости от zip мало толку: делать массивы на каждый чих — дорого.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Конкретно с джаваскриптовским arguments — это плохой пример. аrguments надо было делать экземпляром Array, конечно. Но это просто конкретный косяк дизайна конкретного языка. Вот если бы у нас был тип Array c методом map, и его подтип SubArray, то subArray автоматически наследовал бы поведение, либо его нужно было бы явно переопределять, и тогда SubArray map уже был бы скорей всего неприменим к типу Array

somequest
()
Ответ на: комментарий от x3al

Я не знаю, зачем сюда вообще приплетать понятие ленивости. Она тут не при чем, по большому счету. Каждый вызов генератора возвращает тебе новый объект со значением. Такую фигню можно было и без синтаксического сахара function* сделать, тупо нативными объектами.

somequest
()
Ответ на: комментарий от x3al

У тебя ведь, в этом смысле любой массив ленивый. на каком то шаге взял, и запушил туда новое значение, он у тебя бесконечно растет. Все это ахинея, короче.

somequest
()
Ответ на: комментарий от somequest

У тебя ведь, в этом смысле любой массив ленивый. на каком то шаге взял, и запушил туда новое значение, он у тебя бесконечно растет.

Не бесконечно, а пока RAM хватает. А ленивые последовательности — бесконечно, причём можно проходить их за O(1) по RAM.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Я, как бы немного не о том, я о понятии «ленивость»

Ну, у тебя твой генератор одномоментно стоит на одном из значений бесконечного списка. Ты точно так же можешь создать объект, который при добавлении элемента удаляет предыдущее значение, и инкрементирует счетчик. Вот тебе и будет бесконечный список. К предыдущим значениям уже вернуться нельзя, вторично обойти нельзя, только вперед. Да еще и нет оверхеда на создании нового объекта генератора на каждом шаге.

somequest
()
Ответ на: комментарий от qnikst

У хаскелистов зарплаты мизерные, это сдерживает. А так, да, никаких препятствий. Кто на жабе кодил, легко перейдут на хаскель. Это вопрос времени.

somequest
()
Ответ на: комментарий от somequest

нового объекта генератора на каждом шаге

Наркотики до добра не доводят. Никаких объектов на каждом шаге не создаётся.

x3al ★★★★★
()
Ответ на: комментарий от x3al

не знаю как в пистоне, а в JS создаются

generator = function*(){while(true){yield}}()

object1 = generator.next()
object2 = generator.next()
console.log(object1 == object2) // false

somequest
()
Ответ на: комментарий от somequest

Проблемы JS. В старых firefox-специфичных генераторах раньше не создавались.

И это всё равно быстрее, чем предложенный тобой вариант.

x3al ★★★★★
()
Ответ на: комментарий от x3al

это всё равно быстрее

спорно

count = 1000000

Generator = {
 create: function(){return Object.create(this)},
 next: function(){
    this.handler()
 },
 iter: function(){
   while(true){
     if(this.terminator()) return this.value
     this.next()
   }
 }
}

generator1 = Generator.create()
generator1.value = 0
generator1.handler = function(){this.value++}
generator1.terminator = function(){return this.value === count}

generator2 = function*(){var i = 0; while(i < count){yield i++}}()

console.time("generator1")
  generator1.iter()
console.timeEnd("generator1")

console.time("generator2")
   for(var i of generator2){}
console.timeEnd("generator2")

//generator1: 8ms
//generator2: 135ms

somequest
()
Ответ на: комментарий от umren

Пф, да на любом языке можно написать нечитаемое говно. В то же время, не на каждом можно написать красивый код, который легко читать и понимать.

xenith
()
Ответ на: комментарий от qnikst

Почему? Даже в селектеле хаскелистов всех уволили насколько мне известно

очевидно хаскель не применим в продакшене и все это понимают

umren ★★★★★
()

Дурацкий point-free style. Хрен разберешь, что делает. По-моему лучше так не писать, когда можно написать просто, и все будет понятно сразу с первого взгляда :)

dave ★★★★★
()
Ответ на: комментарий от somequest

Ну нифига у тебя бенчмарки. Ты ничерта не возвращаешь из первого типа-генератора, кроме последнего значения.

x3al ★★★★★
()
Ответ на: комментарий от umren

Потому, что существует много фирм использующих Haskell в production. я не знаю что такое селектел и чем они или хацкелисты, которые там были знамениты.

qnikst ★★★★★
()
Ответ на: комментарий от umren

Сходи напиши его на Go***

package main
 
import "fmt"
import "reflect"
 
func Reduce(in interface{}, memo interface{}, fn func(interface{}, interface{}) interface{}) interface{} {
    val := reflect.ValueOf(in)
 
    for i := 0; i < val.Len(); i++ {
        memo = fn(val.Index(i).Interface(), memo)
    }
 
    return memo
}
 
func main() {
 
    list := []int{1, 2, 3, 4, 5}
 
    result := Reduce(list, 0, func(val interface{}, memo interface{}) interface{} {
        return memo.(int) + val.(int)
    })
 
    fmt.Println(result)
}

Да, источник Idiomatic generics in Go.

monk ★★★★★
() автор топика

func = (. map) . (.) . filter

Аналог однострочника на Perl.

Его только я понять не могу или это аналог однострочника на PERL? Если только я, то посоветуйте, что почитать или как проще понимать такие функции.

Тебе что-то конкретно не понятно? . это оператор, комбинирующий две функции. (. map) это частичное применение этого оператора. Например (/ 2) вернёт функцию одного аргумента, которая будет делить напополам переданный ей аргумент. (.) это функция от двух аргументов, которая применяет оператор .

Конкретно твой пример искусственно переусложнён, чтобы сломать мозг.

Legioner ★★★★★
()
Ответ на: комментарий от qnikst

Ага. Только в Go "(interface{}, interface{}) interface{}) interface{}"

monk ★★★★★
() автор топика
Ответ на: комментарий от Legioner

Конкретно твой пример искусственно переусложнён, чтобы сломать мозг.

Не искусственно. Это (почти) единственный способ написать эту функцию в point-free стиле.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Может быть. А зачем нужен этот стиль? В чём его преимущество перед простой записью в виде лямбда-функции?

Legioner ★★★★★
()
Ответ на: комментарий от monk

Какой смысл писать обфусцированный код, чтобы затем парсить его при помощи отдельных тулз? это же снижает читабельность на десятичные порядки

anonymous
()
Ответ на: комментарий от qnikst

По сравнению с мейнстримом - хаскеля нет в продакшене. Более того - там где он есть, от него последнее время стремительно избавляются.

anonymous
()
Ответ на: комментарий от anonymous

Более того - там где он есть, от него последнее время стремительно избавляются.

А можно ссылочек на подобные истории?

eao197 ★★★★★
()
Ответ на: комментарий от Legioner

А зачем нужен этот стиль? В чём его преимущество перед простой записью в виде лямбда-функции?

Часто удобно и понятно бывает, если не переусердствовать. Например, возьмём код из первой страницы обсуждения и запишем чистыми лямбдами:

revtext1 = unlines . map (unwords . reverse . words) . lines
revtext2 text = unlines (map (\str -> unwords $ reverse (words str)) $ lines text)

Ну вот, код с применением point-free тут сильно читаемее, чем вариант без него.

nezamudich ★★
()
Ответ на: комментарий от Virtuos86

На первой и третьей странице у меня отобразилось четверть комментариев, на второй — один. Просто факт.

Virtuos86 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.