WedX - журнал о программировании и компьютерных науках

Haskell GHCi печатает ленивую последовательность, а Scala REPL — нет.

Я бы распечатал поток чисел, но следующий код выводит только первое число в последовательности:

for ( n <- Stream.from(2) if n % 2 == 0 ) yield println(n)
2
res4: scala.collection.immutable.Stream[Unit] = Stream((), ?)

В Haskell следующее продолжает печатать числа до тех пор, пока не будет прервано, и я хотел бы, чтобы подобное поведение происходило в Scala:

naturals = [1..]
[n | n <- naturals, even n]
[2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,
16.06.2014

  • в основном, это выражение строит Stream из (). Таким образом, вы получаете одно значение, 2, напечатанное только потому, что scala repl нужно было напечатать res4: .... = Stream((), ?), для чего требовалось вычисление только одного (). Если вы удалите yield, это будет другой цикл, и вы должны получить бесконечный поток четных чисел. 16.06.2014
  • Ваш пример на Haskell не печатает числа. Вы можете продемонстрировать это, скомпилировав его из исходного кода, а не используя repl - будет казаться, что он ничего не делает. Реплика Haskell просто отображает содержимое для удобства, а репла Scala — нет. Код на Haskell для вывода бесконечной последовательности четных чисел будет больше похож на forM [n | n <- naturals, even n] (putStrLn . show), где составные функции (putStrLn . show) содержат фактическую логику для вывода числа на экран (так же, как println делает в Scala). 16.06.2014
  • Я получил ответ, просмотрев все ответы: (1) в REPL Scala по умолчанию не печатает бесконечные потоки, поэтому вам нужно явно попросить его напечатать. (2) Чтобы явно попросить его напечатать (либо в REPL, либо в программе): если s является Stream, то используйте s foreach println для печати элементов s по одной строке за раз. (3) Но интерпретатор Scala глуп, и нажатие Ctrl-C убивает интерпретатор (вместо выполняемого вами цикла), поэтому, если s слишком длинное (или бесконечное), вы можете напечатать только ограниченное количество терминов. . 01.10.2016

Ответы:


1

Вместо того, чтобы выводить только println (зачем нужна бесконечная последовательность единиц?):

for ( n <- Stream.from(2) if n % 2 == 0 ) println(n)

Если вам действительно нужна эта бесконечная последовательность юнитов, принудительно введите результат:

val infUnit = for ( n <- Stream.from(2) if n % 2 == 0 ) yield println(n)
infUnit.force // or convert to any other non-lazy collection

Хотя, в конце концов, это приведет к краху программы (из-за большой длины материализованной последовательности).

16.06.2014
  • Тип результата должен быть List[Int], но я экспериментировал с println, чтобы увидеть, что происходит. Пожалуйста, не могли бы вы обновить свой код, чтобы он выводил список целых чисел, как это делает код Haskell? 16.06.2014
  • @И.К. Вы действительно хотите иметь бесконечный список? принудительно или нет? 16.06.2014
  • Наверное, я путаю вопросы. Чтобы уточнить: с Haskell мне действительно не нужно указывать какую-либо дополнительную семантику для создания бесконечного списка целых чисел. Перечисление [1..] прекрасно справляется с этой задачей. Когда я использую этот ленивый список в понимании списка, он продолжает выдавать целые числа, пока интерпретатор Haskell не будет прерван. Я хотел бы такое же поведение в Scala. Я думал, что Stream.from(1) даст мне эту бесконечную ленивую последовательность (без принуждения или указания какой-либо дополнительной семантики), но, похоже, это не так. 16.06.2014
  • В конце концов, код Scala должен повторять поведение Haskell и возвращать List[Int]. 16.06.2014
  • Я думал, что Stream.from(1) даст мне эту бесконечную ленивую последовательность (без принуждения или указания какой-либо дополнительной семантики) Это меня действительно смущает. Stream.from ... действительно даст вам ленивую последовательность -- она ​​настолько ленивая, что не печатает себя полностью. Теперь, чтобы его распечатать, нужно убрать лень, или другими словами заставить его. Да, между ними есть некоторая разница: Haskell по умолчанию ленив, scala по умолчанию активен и, в отличие от Haskell, различает жадный список (он же просто List) и ленивый. Список (он же Stream). 16.06.2014

  • 2

    Тип результата для понимания — это коллекция того же типа, что и коллекция в первом предложении. См. функцию flatMap подпись

    Таким образом, результат

    for ( n <- Stream.from(2) ..... 
    

    представляет собой набор типа Stream[_], ​​который является ленивым, поэтому вам нужно извлекать значения элементов или действия.

    Посмотрите на типы результатов:

    scala> :type for( n <- Stream.from(2)) yield n
    scala.collection.immutable.Stream[Int]
    
    scala> :type for( n <- List(1,2,3)) yield n
    List[Int]
    
    scala> :type for( n <- Set(1,2,3)) yield n
    scala.collection.immutable.Set[Int]
    

    Чтобы распечатать числа до прерывания, попробуйте следующее:

    Stream.from(2).filter(_ % 2 == 0) foreach println
    

    Его тип дает нам, что он будет работать:

    scala> :type Stream.from(2).filter(_ % 2 == 0) foreach println
    Unit
    
    16.06.2014

    3

    Я думаю, вы имели в виду:

    for (n <- Stream.from(2) if n % 2 == 0) yield n
    

    (потому что yield println(n) всегда будет yield () с побочным эффектом печати n)

    Это дает вам коллекцию, которую вы хотите. Однако Scala, в отличие от Haskell, не оценивает все члены (ленивого) списка, когда вы печатаете ленивый список (Stream). Но вы можете преобразовать его в неленивый список, используя .toList. Однако вы не увидите такого же бесконечного поведения печати в Scala, поскольку он попытается сначала построить весь (бесконечный) список, прежде чем вообще что-либо печатать.

    По сути, невозможно получить точно такое же сочетание семантики и поведения в Scala, как в Haskell, при печати бесконечных списков с использованием встроенной инфраструктуры toString.

    P.S.

    for (n <- Stream.from(2) if n % 2 == 0) yield n
    

    короче выражается как

    Stream.from(2).filter(_ % 2 == 0)
    
    16.06.2014
    Новые материалы

    Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что это выглядит сложно…
    Просто начните и учитесь самостоятельно Я хотел выучить язык программирования MVC4, но не мог выучить его раньше, потому что он кажется мне сложным, и я бросил его. Это в основном инструмент..

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

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..


    Для любых предложений по сайту: [email protected]