Чому великі потреби в даних мають функціонувати?


9

Нещодавно я почав працювати над новим проектом, пов’язаним з Big Data, для стажування. Мої менеджери рекомендували почати вивчати функціональне програмування (Вони дуже рекомендують Scala). У мене був принижений досвід використання F #, але я не міг зрозуміти важливості використання цієї парадигми програмування, оскільки це дорого в деяких випадках.

Дін цікаво поговорив на цю тему та поділився своїми думками про те, чому "Big Data" тут: http://www.youtube.com/watch?v=DFAdLCqDbLQ Але це було не дуже зручно, оскільки Big Data не означає тільки Hadoop

Оскільки BigData - це дуже розпливчаста концепція. Я на деякий час це забуваю. Я намагався придумати один простий приклад, щоб порівняти різні аспекти, коли ми маємо справу з даними, щоб побачити, чи є функціональний спосіб дорогим чи ні. Якщо функціональне програмування є дорогим і вимагає багато пам'яті для невеликих даних, то для чого це потрібно для Big Data?

Далеко від фантазійних інструментів я намагався створити рішення для однієї конкретної та популярної проблеми, використовуючи три підходи: імперативний спосіб та функціональний спосіб (рекурсія, використання колекцій). Я порівняв час і складність, щоб порівняти три підходи.

Я використовував Scala для написання цих функцій, оскільки це найкращий інструмент для написання алгоритму, використовуючи три парадигми

def main(args: Array[String]) {
    val start = System.currentTimeMillis()
    // Fibonacci_P
    val s = Fibonacci_P(400000000)
    val end = System.currentTimeMillis()
    println("Functional way: \n the Fibonacci sequence whose values do not exceed four million : %d \n Time : %d ".format(s, end - start))
    val start2 = System.currentTimeMillis()

    // Fibonacci_I
    val s2 = Fibonacci_I(40000000 0)
    val end2 = System.currentTimeMillis();
    println("Imperative way: \n the Fibonacci sequence whose values do not exceed four million : %d \n Time : %d ".format(s2, end2 - start2))
}

Функціональний спосіб:

def Fibonacci_P(max: BigInt): BigInt = {
    //http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Stream
    //lazy val Fibonaccis: Stream[Long] = 0 #:: 1 #:: Fibonaccis.zip(Fibonaccis.tail).map { case (a, b) => a + b }
    lazy val fibs: Stream[BigInt] = BigInt(0)#::BigInt(1)#::fibs.zip(fibs.tail).map {
        n = > n._1 + n._2
    }
    // println(fibs.takeWhile(p => p < max).toList)
    fibs.takeWhile(p = > p < max).foldLeft(BigInt(0))(_ + _)
}

Рекурсивний спосіб:

def Fibonacci_R(n: Int): BigInt = n match {
    case 1 | 2 = > 1
    case _ = > Fibonacci_R(n - 1) + Fibonacci_R(n - 2)
}

Загальний спосіб:

def Fibonacci_I(max: BigInt): BigInt = {
    var first_element: BigInt = 0
    var second_element: BigInt = 1
    var sum: BigInt = 0

    while (second_element < max) {
        sum += second_element

        second_element = first_element + second_element
        first_element = second_element - first_element
    }

    //Return 
    sum
}

Я помітив, що функціональне програмування важке! це займає більше часу і споживає більше місця в пам'яті. Я збентежений, коли я читаю статтю чи дивлюся розмову, вони кажуть, що ми повинні використовувати функціональне програмування в науці даних. Щоправда, простіше та продуктивніше, особливо у світі даних. але це займає більше часу та більше пам’яті.

Отже, чому нам потрібно використовувати функціональне програмування у великих даних? Які найкращі практики використання функціонального програмування (Scala) для великих даних?


5
Функціональне програмування полегшує паралелізацію коду, тому навіть якщо одна операція може зайняти більше часу для виконання однієї нитки, загальна продуктивність може бути кращою завдяки паралельності.
Джорджіо

@Giorgio: Існують різні парадигми як моделювання акторів, щоб отримати найкращі показники паралелізму. Не думаєте так?
user3047512

2
Я думаю, це просто тому, що підхід карт / зменшити від hadoop - це ідея функціонального програмування.
Док Браун

1
@ user3047512: Наприклад, Ерланг використовує модель актора і здебільшого функціональний.
Джорджіо

2
Зв'язок між фейдом "великих даних" та FP не такий простий. У "Великих даних" модним є так званий підхід до зменшення карт , який, в свою чергу, був дещо натхненний етосом функціонального програмування. Тут закінчується схожість, я не бачу подальшого зв’язку між цими двома світами.
SK-логіка

Відповіді:


13

Ось як я це бачу:

  • Давайте на деякий час ігноруємо слова "великі дані", оскільки вони є досить невиразним поняттям

  • Ви згадали Hadoop. Hadoop робить 2 речі: дозволяє мати своєрідний "віртуальний" привід, який розподіляється на декількох машинах із надмірністю, до яких можна отримати доступ через API Hadoop так, як якщо б це був єдиний, єдиний привід. Це називається HDFS, як у розподіленій файловій системі Hadoop . Інша річ, яку робить Hadoop - це дозволяти виконувати завдання Map-Reduce (це рамка для Map-Reduce). Якщо ми перевіримо сторінку Вікіпедії MapReduce , ми побачимо, що:

MapReduce - модель програмування для обробки великих наборів даних з паралельним розподіленим алгоритмом на кластері.

...

Програма MapReduce складається з процедури Map (), яка виконує фільтрацію та сортування (наприклад, сортування учнів за прізвищем у черги, одна черга на кожне ім'я) та процедуру Reduce (), яка виконує підсумкові операції (наприклад, підрахунок числа студентів у кожній черзі, задаючи частоту імен)

...

"MapReduce" - це основа для обробки паралельних задач у величезних наборах даних за допомогою великої кількості комп'ютерів

Також на цій сторінці описано як Hadoop як

Hadoop, безкоштовна та відкрита реалізація Apache з MapReduce.

Тепер Hadoop написаний на Java, що не є функціональною мовою. Крім того, якщо ми заглянемо на сторінку Hadoop, ми також знайдемо приклад того, як створити завдання MapReduce на Java та розгорнути її в кластері Hadoop .

Ось Java-приклад роботи Fibonnaci MapReduce для Hadoop.

Я сподіваюся, що ця відповідь відповідає на ваше запитання, а саме те, що BigData, зокрема робота MapReduce, що створює Фібоначчі, не «потребує» функціонування, але ви можете її реалізувати на мовах ОО, якщо хочете (наприклад).

Звичайно, це не означає, що BigData "потребує" бути лише OO. Ви можете дуже добре використовувати функціональну мову, щоб реалізувати завдання, подібні MapReduce. Наприклад, ви можете використовувати Scala з Hadoop, якщо хочете, за допомогою Scalding .

Інші моменти, які я думаю, варто згадати.

Роблячи рекурсію в Scala, якщо ваш код дозволяє, Scala зробить оптимізацію хвостових викликів . Однак, оскільки JVM не підтримує оптимізацію хвостових викликів , Scala досягає цього, замінюючи під час компіляції ваші рекурсивні дзвінки кодом, еквівалентним циклам, як пояснено тут . Це в основному означає, що робити рекурсивні та нерекурсивні кодові орієнтири за допомогою Scala є безглуздим, оскільки вони обидва в кінцевому підсумку роблять те саме.


2
Ви чудово розумієте, що JVM не підтримує оптимізацію хвостових викликів, що підриває орієнтири, запропоновані ОП. Це дуже інформативна відповідь, дякую.
maple_shaft

1
Дякую за вашу відповідь, так! оптимізація хвостових викликів - одна з прихованих особливостей шкали. stackoverflow.com/questions/1025181/hidden-features-of-scala/… . Однією з проблем «Big Data» є те, що кожна компанія намагається побудувати нову технологію по-різному. Але в основному є два: Hadoop tech та інші. Як ви вже говорили, це суб'єктивне і пов'язане з проблемами, які ми влаштовуємо, ми повинні вибрати правильну парадигму програмування, виходячи також з нашого досвіду. Наприклад: Моделі прогнозування в режимі реального часу не дуже добре працюють на платформах Hadoop.
користувач3047512

9

Поки ви можете запустити його на одній машині, це не "Big Data". Ваш приклад проблема абсолютно недоречна демонструвати щось про це.

Big Data означає, що розміри проблем настільки великі, що розподіл обробки не є оптимізацією, а принциповою вимогою. А функціональне програмування значно спрощує написання правильного та ефективного розподіленого коду через незмінні структури даних та без громадянства.


"Великі дані означають, що розміри проблем настільки великі, що розподіл обробки не є оптимізацією, а принциповою вимогою". - Я не розумію, яку проблему НЕ ВСЕ можна вирішити за допомогою однієї машини, і вимагає принаймні N, де N> 1 ...
Shivan Dragon

6
@ShivanDragon: проблема, яка включає вимоги до продуктивності, які неможливо задовольнити в одній системі. Або там, де розмір даних настільки великий, що жодна система навіть не може його зберегти.
Майкл Боргвардт

Вибачте, зараз я бачу вашу думку. Чи правильно сказати, що ви маєте на увазі, це, зокрема, MapReduce, який живе під парасолькою BigData?
Дракон Шиван

Дякую за ваш внесок, я згоден. Можливо, я не міг знайти хорошого простого прикладу, щоб продемонструвати свою точку зору. "Big Data" - це все ще спосіб, який розробники використовують дані для вирішення наших щоденних проблем, беручи до уваги визначення 3Vs. Я на деякий час забуду 3V і поговорю про дуже простий аспект, що стосується даних. Якщо ми бачимо, що аналізувати дані функціонально, це дорого, чому ми говоримо, що "Big Data" має бути функціональним? Це моя суть.
user3047512

4
@ShivanDragon, наприклад, LHC виробляє кілька гігабайт даних в секунду . Не впевнений, що одна машина може навіть обробляти таку пропускну здатність.
SK-логіка

4

Я не знаю шкали, і тому я не можу коментувати ваш функціональний підхід, але ваш код виглядає як надмірність.

З іншого боку, ваша рекурсивна функція неефективна. Оскільки функція викликає себе двічі, вона є порядком 2 ^ n, що є дуже неефективним. Якщо ви хочете порівняти три підходи, вам потрібно порівняти три оптимальні реалізації.

Функція Фібоначчі може бути реалізована рекурсивно з викликом функції лише один раз. Візьмемо більш узагальнене визначення:

F(0) = f0
F(1) = f1
F(n) = F(n-1) + F(n-2)

Стандартний спеціальний випадок:

f0 = 0
f1 = 1

Загальна рекурсивна функція:

function fibonacci($f0, $f1, $n){
    if($n < 0 || !isInt($n)) return false;
    if($n = 0) return $f0;
    if($n = 1) return $f1;
    return fibonacci($f1, $f0 + $f1, $n - 1);
}

Дякую! Ви підняли хорошу точку, але не існує ефективного способу зробити це ітераційним шляхом. Це дуже поширена проблема (набір Фібоначчі). і це сенс вирішити цю проблему трьома способами. Чи можете ви запропонувати кращий спосіб вирішити цю проблему за допомогою будь-якої мови програмування, я можу повторно написати це за допомогою scala і зробити ті ж тести?
user3047512

@ user3047512 Для мови, яка підтримує хвостову рекурсію, ви можете записати її за допомогою акумулятора. Приклади
тости_флейки

Scala також підтримує рекурсію хвоста як приховану функцію oldfashionedsoftware.com/2008/09/27/…
user3047512

1
@ user3047512 Оскільки рекурсивне рішення є чистою функцією (вихід залежить тільки від аргументів функції та нічого іншого ), запам'ятовування є хорошим рішенням. Простіше кажучи, кожен раз, коли він повертає значення, зберігає аргументи і призводить до отримання хеша ключа / значення, і кожного разу, коли функція виконується, перегляньте її спочатку. Це одна з переваг чистих функцій - майбутній виклик цієї функції знайде попередньо хешоване значення та зробить нульові обчислення, тому що ми знаємо, що результат не змінився.
Ізката

@ user3047512 Ітеративна версія також виглядає як чиста функція в цьому випадку, але це не завжди так - на функціональній мові, я вважаю, що це краще застосовується мовою ...
Izkata

0

Якщо функціональне програмування є дорогим і вимагає багато пам'яті для невеликих даних, то для чого це потрібно для Big Data?

Зокрема, я вже бачу декілька додатків, де це надзвичайно корисно. колишній Статистика, тобто обчислення функції Гаусса на ходу з різними параметрами або набором параметрів для аналізу даних. Існує також інтерполяція для чисельного аналізу тощо.

Які найкращі практики використання функціонального програмування (Scala) для великих даних?

Для відповіді на ефективність існують також методи, які допомагають підвищити ефективність у просторі чи часі, зокрема рекурсія, рекурсія хвоста , стиль проходження продовження , функції вищого порядку тощо. Деякі мови мають свої плюси і мінуси (наприклад, ледачий проти прагнення). щось таке, як, наприклад, послідовність фібоначків, я можу просто використовувати імперативний спосіб, бо часом можу виявити, що хтось із моїх колег неохоче і може бути не таким комфортним у функціональному програмуванні, а значить, займає більше часу на розробку ... (я все одно вважаю за краще використовувати функціональне програмування, коли я можу [додатки, за які я відповідаю]), оскільки я вважаю його швидким, чистим та "легким для читання" (хоча я вважаю цей суб'єктивний) код.

У Вікіпедії розміщена "швидка" версія послідовності фібоначків. https://en.wikipedia.org/wiki/Functional_programming#Scala

def fibTailRec(n: Int): Int = {
  @tailrec def f(a: Int, b: Int, c: Int): Int = if (a == 0) 0 else if(a < 2) c else f(a-1, c, b + c)
  f(n, 0, 1)
}

Використання потоків / хоф

val fibStream:Stream[Int] = 0 #:: 1 #:: (fibStream zip fibStream.tail).map{ t => t._1 + t._2 }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.