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


744

Что такое лямбда в мире компьютерных наук для человека без опыта работы в области компьютерных технологий?


3
Выражение amda здесь объяснено красиво.
Джеймир Мулани,

1
см это для того, как лямбда - функции выглядят на разных языках, и в каком году они были введены в этих языках.
Филипп Шварц

Ответы:


1082

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

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

питон

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

Как видно из фрагмента Python, функция adder принимает аргумент 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();

Haskell

(\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

Lua

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

Ruby - это Ruby, для ламбд есть сокращение, поэтому вы можете определить adderэто следующим образом:

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

р

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

3
В чем же разница между лямбда-функцией и функтором ?
Maxpm

1
@Maxpm функтор может быть стандартным объектом с полями и функциями экземпляра, тогда как лямбда-функция обычно состоит только из одной строки инструкций. Это может варьироваться в зависимости от языка, конечно.
zdimension

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

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

@AliAnkarali или привыкнуть к использованию Rubys Lambda;)
Джимми М.Г. Лим

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));для первого примера.
23

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

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

72

Название «лямбда» - это просто исторический артефакт. Все, о чем мы говорим, это выражение, значением которого является функция.

Простой пример (используя 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

52

Лямбда-исчисление является последовательной математической теорией замещения. В школьной математике можно увидеть, например, в 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; 
    } );

У него также есть встроенная функция, но на этот раз это просто магия компилятора - компилятор C # разделит это на новый метод экземпляра с автоматически сгенерированным именем.

Анонимные методы не могут быть прочитаны, и поэтому логика не может быть переведена, как это возможно для лямбда-функций.


7

Мне нравится объяснение Lambdas в этой статье: Эволюция LINQ и ее влияние на дизайн C # . Это имело большой смысл для меня, поскольку показывает реальный мир для Lambdas и создает его в качестве практического примера.

Их быстрое объяснение: лямбда-выражения - это способ обработки кода (функций) как данных.


7

Пример лямбды в Ruby выглядит следующим образом:

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

hello.call

Будет генерировать следующий вывод:

Hello
I am inside a proc

6

На вопрос формально дан большой ответ, поэтому я не буду пытаться добавить больше к этому.

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

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


5

@ Брайан Я все время использую лямбды в 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 также можно передать оператору цитаты, а затем проследить в виде списка списков. Некоторые мощные макросы сделаны таким образом.



4

Просто потому, что я не могу увидеть пример C ++ 11 здесь, я продолжу и выложу этот хороший пример отсюда . После поиска, это самый ясный пример конкретного языка, который я смог найти.

Здравствуйте, Lambdas, версия 1

template<typename F>

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

Здравствуйте, Lambdas, версия 2:

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

3

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

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

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

Одно из определенных преимуществ использования формальной лямбды - это (я предполагаю) проверка во время компиляции: Fox не будет знать, если вы опечатаете текстовую строку выше, пока не попытается ее запустить.

Это также полезно для кода, управляемого данными: вы можете хранить целые процедуры в памятных полях в базе данных, а затем просто оценивать их во время выполнения. Это позволяет настраивать часть приложения, фактически не имея доступа к источнику. (Но это совсем другая тема.)


3

Что такое лямбда в мире компьютерных наук для человека без опыта работы в области компьютерных технологий?

Я проиллюстрирую это интуитивно шаг за шагом в простых и удобочитаемых кодах Python.

Короче говоря, лямбда - это просто анонимная и встроенная функция.

Давайте начнем с задания, чтобы понять, 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

Он работает для простых операторов, в Python 7 их 11 типов . Простые операторы - документация 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 с методом sorted () - от обычной функции до кратчайшего выражения:

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. Однако иногда мне трудно читать :(

Также я сделал интересную функцию forEach:

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.