Що таке лямбда (функція)?


743

Для людини, яка не має наукового походження, що таке лямбда у світі інформатики?


3
вираз амда пояснив тут прекрасно.
Джеймер Мулані

1
див. це, як виглядають лямбда-функції на різних мовах і в якому році вони були введені цими мовами.
Філіп Шварц

Відповіді:


1081

Лямбда походить від обчислення Ламбди і посилається на анонімні функції в програмуванні.

Чому це круто? Це дозволяє записувати функції швидкого викидання, не називаючи їх. Це також забезпечує хороший спосіб написання закриттів. З цією силою ви можете робити такі речі.

Пітон

def adder(x):
    return lambda y: x + y
add5 = adder(5)
add5(1)
6

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

Приклади іншими мовами

Perl 5

sub adder {
    my ($x) = @_;
    return sub {
        my ($y) = @_;
        $x + $y
    }
}

my $add5 = adder(5);
print &$add5(1) == 6 ? "ok\n" : "not ok\n";

JavaScript

var adder = function (x) {
    return function (y) {
        return x + y;
    };
};
add5 = adder(5);
add5(1) == 6

JavaScript (ES6)

const adder = x => y => x + y;
add5 = adder(5);
add5(1) == 6

Схема

(define adder
    (lambda (x)
        (lambda (y)
           (+ x y))))
(define add5
    (adder 5))
(add5 1)
6

C # 3.5 або вище

Func<int, Func<int, int>> adder = 
    (int x) => (int y) => x + y; // `int` declarations optional
Func<int, int> add5 = adder(5);
var add6 = adder(6); // Using implicit typing
Debug.Assert(add5(1) == 6);
Debug.Assert(add6(-1) == 5);

// Closure example
int yEnclosed = 1;
Func<int, int> addWithClosure = 
    (x) => x + yEnclosed;
Debug.Assert(addWithClosure(2) == 3);

Швидкий

func adder(x: Int) -> (Int) -> Int{
   return { y in x + y }
}
let add5 = adder(5)
add5(1)
6

PHP

$a = 1;
$b = 2;

$lambda = fn () => $a + $b;

echo $lambda();

Хаскелл

(\x y -> x + y) 

Ява дивіться цю публікацію

// The following is an example of Predicate : 
// a functional interface that takes an argument 
// and returns a boolean primitive type.

Predicate<Integer> pred = x -> x % 2 == 0; // Tests if the parameter is even.
boolean result = pred.test(4); // true

Луа

adder = function(x)
    return function(y)
        return x + y
    end
end
add5 = adder(5)
add5(1) == 6        -- true

Котлін

val pred = { x: Int -> x % 2 == 0 }
val result = pred(4) // true

Рубін

Ruby трохи відрізняється тим, що ви не можете викликати лямбда, використовуючи абсолютно той самий синтаксис, що і виклик функції, але вона все ще має лямбда.

def adder(x)
  lambda { |y| x + y }
end
add5 = adder(5)
add5[1] == 6

Рубі, будучи Рубі, є скороченням лямбд, тому можна визначити adderтакий спосіб:

def adder(x)
  -> y { x + y }
end

R

adder <- function(x) {
  function(y) x + y
}
add5 <- adder(5)
add5(1)
#> [1] 6

3
Яка різниця між функцією лямбда та функтором ?
Maxpm

1
@Maxpm функтор може бути стандартним об'єктом із полями екземплярів і функціями, тоді як функція лямбда зазвичай складається лише з одного рядка інструкцій. Це може змінюватися залежно від мови курсу.
здіменція

1
Я не думаю, що це обов'язково точно сказати, що лямбда-функції такі ж, як анонімні функції. Для деяких мов, таких як JavaScript, лямбда-вираз є специфічною формою анонімної функції. Приклад JavaScript, який ви дали, - це анонімна функція без лямбда-синтаксису, тоді як приклад JavaScript (ES6), який ви дали, - це лямбда-вираз.
Кайл Делані

1
@KyleDelaney, дійсно, анонімність не є необхідною умовою бути лямбдаю, дійсно є функція лямбда, яка не є анонімною, як ви вказуєте на неї навіть у прикладі
Carmine Tambascia

@AliAnkarali або звикнути до використання rubys lambda;)
Jimmy MG Lim

107

Лямбда - це тип функції, визначений вбудованим. Поряд з лямбда, у вас зазвичай також є якийсь тип змінної, який може містити посилання на функцію, лямбда або інше.

Наприклад, ось код C #, який не використовує лямбда:

public Int32 Add(Int32 a, Int32 b)
{
    return a + b;
}

public Int32 Sub(Int32 a, Int32 b)
{
    return a - b;
}

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, Add);
    Calculator(10, 23, Sub);
}

Це викликає калькулятор, проходячи не лише два числа, але який метод викликати всередині калькулятора, щоб отримати результати обчислення.

У C # 2.0 ми отримали анонімні методи, що скорочує вищевказаний код до:

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, delegate(Int32 a, Int32 b)
    {
        return a + b;
    });
    Calculator(10, 23, delegate(Int32 a, Int32 b)
    {
        return a - b;
    });
}

А потім у C # 3.0 у нас з'явилися лямбда, що робить код ще коротшим:

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, (a, b) => a + b);
    Calculator(10, 23, (a, b) => a - b);
}

Замість того, щоб чітко визначити делегата Op, можна просто використовуватиFunc<int, int>
Mateen Ulhaq

Я б запропонував Console.WriteLine("Calculator: op " + op.Method.Name + " (" + a + ", " + b + ") = " + op(a, b));перший приклад.
Марк.2377,

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

Дякую за Ваш приклад, що більш складна функція допомагає зрозуміти переваги лямбдатів набагато більше, ніж прості функції, коли, схоже, ви не отримаєте великої оптимізації
Сара

71

Назва "лямбда" - просто історичний артефакт. Все, про що ми говоримо, - це вираз, значення якого - функція.

Простий приклад (використовуючи Scala для наступного рядка):

args.foreach(arg => println(arg))

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

void printThat(Object that) {
  println(that)
}
...
args.foreach(printThat)

за винятком того, що вам не потрібно займатись:

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

Після того, як ви звикли функціонувати значеннями, обходитися без них здається нерозумним, як і потрібно називати кожен вираз, наприклад:

int tempVar = 2 * a + b
...
println(tempVar)

замість того, щоб просто написати вираз там, де вам це потрібно:

println(2 * a + b)

Точне позначення варіюється від мови до мови; Грецька мова не завжди потрібна! ;-)


62

Він посилається на обчислення лямбда , це формальна система, яка просто має лямбда-вирази, які представляють функцію, яка приймає функцію за єдиний аргумент і повертає функцію. Всі функції в лямбдальному обчисленні є такого типу, тобтоλ : λ → λ .

Лісп використовував концепцію лямбда, щоб називати буквари анонімної функції. Ця лямбда являє собою функцію, яка бере два аргументи, x і y, і повертає їх добуток:

(lambda (x y) (* x y)) 

Він може бути застосований так, як це (оцінюється до 50 ):

((lambda (x y) (* x y)) 5 10)

Я думаю, що ваше використання λ : λ -> λє заплутаним (і насправді недійсним).
einpoklum

51

Обчислення лямбда - це послідовна математична теорія заміщення. У шкільній математиці можна побачити, наприклад, в x+y=5паріx−y=1 . Поряд із способами маніпулювання окремими рівняннями також можна зібрати інформацію з цих двох, якщо заміна перехресних рівнянь виконана логічно. Обчислення лямбда кодифікує правильний спосіб зробити ці заміни.

Враховуючи, що y = x−1це дійсна перестановка другого рівняння, це: λ y = x−1означає функцію, що замінює символи x−1для символу y. Тепер уявіть, що ви застосовуєте λ yдо кожного терміна в першому рівнянні. Якщо термін є, yтоді виконуйте заміну; інакше нічого не робити. Якщо це зробити на папері, ви побачите, як це застосуватиλ y зробить перше рівняння вирішуваним.

Це відповідь без жодної інформатики чи програмування.

Найпростіший приклад програмування, який я можу придумати, походить з http://en.wikipedia.org/wiki/Joy_(programming_language)#How_it_works :

ось як може бути визначена функція квадрата в імперативній мові програмування (C):

int square(int x)
{
    return x * x;
}

Змінна x - це формальний параметр, який замінюється фактичним значенням, яке має бути квадратним, коли викликається функція. У функціональній мові (схемі) буде визначена та сама функція:

(define square
  (lambda (x) 
    (* x x)))

Це багато в чому відрізняється, але він все одно використовує формальний параметр x таким же чином.


Додано: http://imgur.com/a/XBHub

лямбда


14

Трохи спрощена: лямбда-функція - це та функція, яку можна передати в інші функції, і це доступ до логіки.

Синтаксис C # лямбда часто компілюється з простими методами так само, як і анонімні делегати, але він також може бути розбитий і зчитана його логіка.

Наприклад (у C # 3):

LinqToSqlContext.Where( 
    row => row.FieldName > 15 );

LinqToSql може прочитати цю функцію (x> 15) та перетворити її у фактичний SQL для виконання за допомогою дерев виразів.

Заява вище:

select ... from [tablename] 
where [FieldName] > 15      --this line was 'read' from the lambda function

Це відрізняється від звичайних методів або анонімних делегатів (які насправді просто магія компілятора), оскільки їх неможливо прочитати .

Не всі методи в C #, які використовують лямбда-синтаксис, можуть бути складені до дерев виразів (тобто власне лямбда-функцій). Наприклад:

LinqToSqlContext.Where( 
    row => SomeComplexCheck( row.FieldName ) );

Тепер дерево виразів неможливо прочитати - SomeComplexCheck не можна розбити. Оператор SQL виконуватиметься без того, де і кожен рядок даних буде прокладений SomeComplexCheck.

Функції лямбда не слід плутати з анонімними методами. Наприклад:

LinqToSqlContext.Where( 
    delegate ( DataRow row ) { 
        return row.FieldName > 15; 
    } );

Це також має функцію 'inline', але на цей раз це просто магія компілятора - компілятор C # розділить це на новий метод примірника з автогенерованою назвою.

Не можна читати анонімні методи, і тому логіка не може бути перетворена, як це можливо для лямбда-функцій.


7

Мені подобається пояснення Лямбда в цій статті: Еволюція LINQ та її вплив на дизайн C # . Для мене це мало сенс, оскільки він показує реальний світ для Ламбдаса і створює його як практичний приклад.

Їх швидке пояснення: Лямбди - це спосіб трактувати код (функції) як дані.


7

Приклад лямбда в Рубі:

hello = lambda do
    puts('Hello')
    puts('I am inside a proc')
end

hello.call

Створить такий вихід:

Hello
I am inside a proc

5

@Brian Я використовую лямбдаки весь час в C #, в операторах LINQ і не LINQ. Приклад:

string[] GetCustomerNames(IEnumerable<Customer> customers)
 { return customers.Select(c=>c.Name);
 }

Перш ніж C #, я використовував анонімні функції в JavaScript для зворотного виклику функцій AJAX до того, як термін Ajax навіть був уведений:

getXmlFromServer(function(result) {/*success*/}, function(error){/*fail*/});

Цікавим із синтаксису лямбда C # є те, що самостійно їх тип не може бути заражений (тобто ви не можете вводити var foo = (x, y) => x * y), але залежно від того, який тип вони вони будуть складені у вигляді делегатів або абстрактних дерев синтаксису, що представляють вираз (саме так, як картографи об’єктів LINQ роблять свою магію "інтегрованої мови").

Лямбди в LISP також можуть бути передані оператору котирування, а потім пройти як список списків. Деякі потужні макроси зроблені таким чином.


5

На запитання формально дано відповідь, тому я не намагатимусь додати більше цього питання.

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

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



4

Тільки тому, що я не можу побачити приклад C ++ 11 тут, я продовжу та розміщу цей приємний приклад звідси . Після пошуку - це найясніший приклад мови, який я міг знайти.

Привіт, Лямбдас, версія 1

template<typename F>

void Eval( const F& f ) {
        f();
}
void foo() {
        Eval( []{ printf("Hello, Lambdas\n"); } );
}

Привіт, Лямбдас, версія 2:

void bar() {
    auto f = []{ printf("Hello, Lambdas\n"); };
    f();
}

3

У мене виникають проблеми з обгортанням лямбда-виразів, оскільки я працюю в Visual FoxPro, який має функції Macro substitution та ExecScript {} і Evaluate (), які, здається, служать майже одній і тій же цілі.

? Calculator(10, 23, "a + b")
? Calculator(10, 23, "a - b");

FUNCTION Calculator(a, b, op)
RETURN Evaluate(op)

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

Це також корисно для керованого даних коду: ви можете зберігати цілі підпрограми в полях пам'яток у базі даних, а потім просто оцінювати їх під час виконання. Це дозволяє налаштувати частину програми, не маючи фактичного доступу до джерела. (Але це взагалі інша тема.)


3

Для людини, яка не має наукового походження, що таке лямбда у світі інформатики?

Я проілюструю це інтуїтивно, крок за кроком, у простих і читабельних кодах пітона.

Коротше кажучи, лямбда - це лише анонімна і вбудована функція.

Почнемо з завдання, щоб зрозуміти lambdasпершокурсника з фоном основної арифметики.

План присвоєння - "ім'я = значення", див.

In [1]: x = 1
   ...: y = 'value'
In [2]: x
Out[2]: 1
In [3]: y
Out[3]: 'value'

'x', 'y' - це імена, а 1, 'value' - значення. Спробуйте функцію з математики

In [4]: m = n**2 + 2*n + 1
NameError: name 'n' is not defined

Звіти про помилки,
ви не можете записати математику безпосередньо як код, 'n' слід визначити або призначити значення.

In [8]: n = 3.14
In [9]: m = n**2 + 2*n + 1
In [10]: m
Out[10]: 17.1396

Це працює зараз, що робити, якщо ви наполягаєте на поєднанні двох окремо розташованих ліній в одну. Приходитьlambda

In [13]: j = lambda i: i**2 + 2*i + 1
In [14]: j
Out[14]: <function __main__.<lambda>>

Про помилки не повідомлялося.

Це погляд lambda, це дозволяє вам записати функцію в один рядок, як це робити математично в комп'ютер безпосередньо.

Ми це побачимо пізніше.

Давайте продовжимо копати глибше на «завдання».

Як показано вище, символ рівності =працює для простого типу даних (1 і 'значення') та простого виразу (n ** 2 + 2 * n + 1).

Спробуйте це:

In [15]: x = print('This is a x')
This is a x
In [16]: x
In [17]: x = input('Enter a x: ')
Enter a x: x

Він працює для простих операторів, їх є 11 типів у python 7. Прості оператори - документація Python 3.6.3

Як щодо складної заяви,

In [18]: m = n**2 + 2*n + 1 if n > 0
SyntaxError: invalid syntax
#or
In [19]: m = n**2 + 2*n + 1, if n > 0
SyntaxError: invalid syntax

Існує defможливість включити його в роботу

In [23]: def m(n):
    ...:     if n > 0:
    ...:         return n**2 + 2*n + 1
    ...:
In [24]: m(2)
Out[24]: 9

Тада, проаналізуй це, 'm' - це ім'я, 'n ** 2 + 2 * n + 1' - значення. :є варіантом '='.
Знайдіть це, якщо тільки для розуміння, все починається з присвоєння, і все - це завдання.

Тепер повернемося до lambda, у нас є функція під назвою 'm'

Спробуйте:

In [28]: m = m(3)
In [29]: m
Out[29]: 16

Тут є два імені 'm', функція mвже має ім'я, дубльоване.

Це форматування:

In [27]: m = def m(n):
    ...:         if n > 0:
    ...:             return n**2 + 2*n + 1
    SyntaxError: invalid syntax

Це не розумна стратегія, тому повідомляють про помилки

Треба видалити одну з них, встановити функцію без імені.

m = lambda n:n**2 + 2*n + 1

Це називається "анонімна функція"

На закінчення

  1. lambda у вбудованій функції, яка дозволяє записати функцію в один прямий рядок, як це робиться в математиці
  2. lambda є анонімним

Сподіваюся, це допомагає.


2

Це функція, яка не має імені. Наприклад, в c # ви можете використовувати

numberCollection.GetMatchingItems<int>(number => number > 5);

повернути числа, що перевищують 5.

number => number > 5

є лямбда частина тут. Він являє собою функцію, яка приймає параметр (число) і повертає булеве значення (число> 5). Метод GetMatchingItems використовує цю лямбда на всіх елементах колекції та повертає відповідні елементи.


2

В Javascript, наприклад, функції розглядаються як же змішаного типу , як і все інше ( int, string, float, bool). Таким чином, ви можете створювати функції на ходу, призначати їх речам і передзвонити їм пізніше. Це корисно, але не те, що ви хочете надмірно використовувати, або ви будете плутати всіх, хто повинен підтримувати ваш код після вас ...

Це якийсь код, з яким я грав, щоб побачити, наскільки глибоко йде ця кроляча нора:

var x = new Object;
x.thingy = new Array();
x.thingy[0] = function(){ return function(){ return function(){ alert('index 0 pressed'); }; }; }
x.thingy[1] = function(){ return function(){ return function(){ alert('index 1 pressed'); }; }; }
x.thingy[2] = function(){ return function(){ return function(){ alert('index 2 pressed'); }; }; }

for(var i=0 ;i<3; i++)
    x.thingy[i]()()();

2

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

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


2

A Lambda Functionабо a Small Anonymous Function- це автономний блок функціональності, який можна передавати і використовувати у вашому коді. Ламбда має різні імена на різних мовах програмування - Lambdaу Python та Kotlin , Closureу Swift або Blockу C та Objective-C . Хоча значення лямбда досить схоже для цих мов, воно іноді має незначні відмінності.

Подивимось, як працює Lambda (Closure) у Swift 4.2 методом сортування () - від нормальної функції до найкоротшого виразу:

let coffee: [String] = ["Cappuccino", "Espresso", "Latte", "Ristretto"]

1. Нормальна функція

func backward(_ n1: String, _ n2: String) -> Bool {
    return n1 > n2
}
var reverseOrder = coffee.sorted(by: backward)


// RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"]

2. Вираз закриття

reverseOrder = coffee.sorted(by: { (n1: String, n2: String) -> Bool in
    return n1 > n2
})

3. Вбудований вираз закриття

reverseOrder = coffee.sorted(by: { (n1: String, n2: String) -> Bool in return n1 > n2 } )

4. Тип спонукання з контексту

reverseOrder = coffee.sorted(by: { n1, n2 in return n1 > n2 } )

5. Неявне повернення з одновиражних замикань

reverseOrder = coffee.sorted(by: { n1, n2 in n1 > n2 } )

6. Короткі назви аргументів

reverseOrder = coffee.sorted(by: { $0 > $1 } )

// $0 and $1 are closure’s first and second String arguments.

7. Операторські методи

reverseOrder = coffee.sorted(by: >)

// RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"]

Сподіваюсь, це допомагає.


1

Я теж це отримав. Я спробував це в JS з цим:

var addAndMult = function(x) {
        return (function(y) {
            return (function(z) {
                return (x+y)*z; 
                });
            });
        };

Він додає від 2 до 4, а результат зменшує на 6. Однак мені іноді важко читати :(

Також я зробив цікаву функцію для кожного:

var forEach = function(arr) {
            return (function(x) {
            for (var i=0; arr[i]; i++) {
                 x(arr[i]);
             }
        });
    }

forEach ([1,2,3,4,5]) (console.log);

Цей метод повторює масив і виконує дію - у випадку друку на консолі. Тепер я теж розумію, чому лабораторії потужні.


1

У комп'ютерному програмуванні лямбда - це фрагмент коду (висловлювання, вираз або група з них), який бере деякі аргументи із зовнішнього джерела. Це не завжди має бути анонімною функцією - у нас є багато способів їх реалізації.

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

Слово "функція" в програмуванні також відрізняється - у нас "функція - це ряд кроків, які потрібно зробити" (від латинського "виконувати"). У математиці мова йде про співвідношення змінних.

Функціональні мови намагаються бути максимально схожими на математичні формули, а їх слова означають майже те саме. Але в інших мовах програмування у нас вона інша.


0

На питання відповіли повністю, я не хочу вникати в подробиці. Я хочу поділитися використанням при написанні чисельних обчислень в іржі.

Є приклад лямбда (анонімна функція)

let f = |x: f32| -> f32 { x * x - 2.0 };
let df = |x: f32| -> f32 { 2.0 * x };

Коли я писав модуль методу Ньютона – Рафсона, його використовували як похідне першого та другого порядку. (Якщо ви хочете знати, що таке метод Ньютона – Рафсона, відвідайте " https://en.wikipedia.org/wiki/Newton%27s_method ".

Вихід такий

println!("f={:.6}      df={:.6}", f(10.0), df(10.0))

f=98.000000       df=20.000000

0

Уявіть, що у вас є ресторан з можливістю доставки, і у вас є замовлення, яке потрібно зробити за 30 хвилин. Справа в тому, що клієнтів, як правило, не хвилює, якщо ви відправляєте їжу на велосипеді з машиною або босоніж, доки ви не будете тримати їжу теплою і підв'язаною. Тож давайте перетворимо цю ідіому в Javascript з анонімними та визначеними транспортними функціями.

Нижче ми визначили спосіб нашої доставки ака, ми визначаємо ім'я функції:

// ES5 
var food = function withBike(kebap, coke) {
return (kebap + coke); 
};

Що робити, якщо ми використовуємо функції стрілки / лямбда для здійснення цієї передачі:

// ES6    
const food = (kebap, coke) => { return kebap + coke };

Ви бачите, що немає різниці для клієнта і не витрачаєте часу на роздуми про те, як відправити їжу. Просто надішліть.

До речі, я не рекомендую шашлик з коксом, тому верхні коди дадуть вам помилки. Весело.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.