Как можно сделать что-либо полезное без изменяемого состояния?

используйте этот код:

public void deleteAll()
{
    SQLiteDatabase db = this.getWritableDatabase();
    db.execSQL("delete from "+ TABLE_NAME);
    db.close();
}

вы можете добавить его в класс DatabaseHandler, это полный исходный код:

public class DatabaseHandler extends SQLiteOpenHelper {

// All Static variables
// Database Version
private static final int DATABASE_VERSION = 1;

// Database Name
public static final String DATABASE_NAME = "OffLineMessagesClient";

// Contacts table name
public static final String TABLE_NAME = "messages_client";

// Contacts Table Columns names
private static final String KEY_ENTITY_ID = "entity_id";
private static final String KEY_MESSAGE = "message";

public DatabaseHandler(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

// Creating Tables
@Override
public void onCreate(SQLiteDatabase db) {
    String CREATE_CONTACTS_TABLE = "CREATE TABLE " + TABLE_NAME + "("
            + KEY_ENTITY_ID + " INTEGER,"
            + KEY_MESSAGE + " TEXT"
            +  ")";

    db.execSQL(CREATE_CONTACTS_TABLE);
}

// Upgrading database
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion,
                      int newVersion) {
    // Drop older table if existed
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);

    // Create tables again
    onCreate(db);
}

public void deleteAll()
{
    SQLiteDatabase db = this.getWritableDatabase();
    db.execSQL("delete from "+ TABLE_NAME);
    db.close();
}
}
249
задан Rex M 25 June 2009 в 17:44
поделиться

13 ответов

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

Если вам интересно, вот серия статей, описывающих программирование игр с помощью Erlang.

Вам, вероятно, не понравится этот ответ, но не ] получить функциональную программу, пока не воспользуетесь ею. Я могу опубликовать образцы кода и сказать: «Вот, см. » - но если вы не понимаете синтаксиса и основных принципов, ваши глаза просто тускнеют. С вашей точки зрения, похоже, что я делаю то же самое, что и императивный язык, но просто устанавливаю всевозможные границы, чтобы целенаправленно усложнять программирование. С моей точки зрения, вы просто переживаете парадокс Блуба .

Сначала я был настроен скептически, но несколько лет назад я вскочил на поезд функционального программирования и влюбился в него. Уловка с функциональным программированием заключается в том, чтобы распознавать шаблоны, конкретные назначения переменных и перемещать императивное состояние в стек. Например, цикл for становится рекурсией:

// Imperative
let printTo x =
    for a in 1 .. x do
        printfn "%i" a

// Recursive
let printTo x =
    let rec loop a = if a <= x then printfn "%i" a; loop (a + 1)
    loop 1

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

// Preferred
let printTo x = seq { 1 .. x } |> Seq.iter (fun a -> printfn "%i" a)

Метод Seq.iter будет перечислять через коллекцию и вызывать анонимную функцию для каждого элемента. Очень удобно :)

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

// imperative version
pacman = new pacman(0, 0)
while true
    if key = UP then pacman.y++
    elif key = DOWN then pacman.y--
    elif key = LEFT then pacman.x--
    elif key = UP then pacman.x++
    render(pacman)

// functional version
let rec loop pacman =
    render(pacman)
    let x, y = switch(key)
        case LEFT: pacman.x - 1, pacman.y
        case RIGHT: pacman.x + 1, pacman.y
        case UP: pacman.x, pacman.y - 1
        case DOWN: pacman.x, pacman.y + 1
    loop(new pacman(x, y))

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

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

Практически каждое пользовательское приложение я можно думать о состоянии как о ядре concept.

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

using System;

namespace ConsoleApplication1
{
    static class Stack
    {
        public static Stack<T> Cons<T>(T hd, Stack<T> tl) { return new Stack<T>(hd, tl); }
        public static Stack<T> Append<T>(Stack<T> x, Stack<T> y)
        {
            return x == null ? y : Cons(x.Head, Append(x.Tail, y));
        }
        public static void Iter<T>(Stack<T> x, Action<T> f) { if (x != null) { f(x.Head); Iter(x.Tail, f); } }
    }

    class Stack<T>
    {
        public readonly T Head;
        public readonly Stack<T> Tail;
        public Stack(T hd, Stack<T> tl)
        {
            this.Head = hd;
            this.Tail = tl;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Stack<int> x = Stack.Cons(1, Stack.Cons(2, Stack.Cons(3, Stack.Cons(4, null))));
            Stack<int> y = Stack.Cons(5, Stack.Cons(6, Stack.Cons(7, Stack.Cons(8, null))));
            Stack<int> z = Stack.Append(x, y);
            Stack.Iter(z, a => Console.WriteLine(a));
            Console.ReadKey(true);
        }
    }
}

Приведенный выше код создает два неизменяемых списка, складывает их вместе, чтобы создать новый список, и добавляет результаты. В приложении не используется изменяемое состояние. Это выглядит немного громоздко, но это только потому, что C # - многословный язык. Вот эквивалентная программа на F #:

type 'a stack =
    | Cons of 'a * 'a stack
    | Nil

let rec append x y =
    match x with
    | Cons(hd, tl) -> Cons(hd, append tl y)
    | Nil -> y

let rec iter f = function
    | Cons(hd, tl) -> f(hd); iter f tl
    | Nil -> ()

let x = Cons(1, Cons(2, Cons(3, Cons(4, Nil))))
let y = Cons(5, Cons(6, Cons(7, Cons(8, Nil))))
let z = append x y
iter (fun a -> printfn "%i" a) z

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

Используя более важный пример, я также написал этот синтаксический анализатор SQL , который полностью не имеет состояния (или, по крайней мере, мой код не имеет состояния, я не знаю, Библиотека lexing не имеет состояния).

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

155
ответ дан 23 November 2019 в 02:59
поделиться
1
ответ дан 23 November 2019 в 02:59
поделиться

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

7
ответ дан 23 November 2019 в 02:59
поделиться

Таким образом FORTRAN будет работать без ОБЩИХ блоков: вы должны написать методы, которые имеют переданные вами значения и локальные переменные. Вот и все.

Объектно-ориентированное программирование объединило нас между состоянием и поведением, но это была новая идея, когда я впервые столкнулся с ней в C ++ еще в 1994 году.

Боже, я был функциональным программистом, когда был инженером-механиком и Я этого не знал!

3
ответ дан 23 November 2019 в 02:59
поделиться

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

Таким образом можно смоделировать даже глобальное изменяемое состояние. В Haskell, например, программа - это функция от мира к миру. То есть вы передаете в всю вселенную , и программа возвращает новую вселенную. Однако на практике вам нужно пройти только те части вселенной, которые действительно интересны вашей программе. И программы фактически возвращают последовательность действий , которые служат в качестве инструкций для операционной среды, в которой выполняется программа.

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

Рассмотрим этот код:

int x = 1;
int y = x + 1;
x = x + y;
return x;

Довольно стандартный императивный код. Ничего интересного не делает, но это нормально для иллюстрации. Думаю, вы согласитесь, что здесь замешано государство. Значение переменной x изменяется со временем. Теперь давайте немного изменим обозначения, изобретя новый синтаксис:

let x = 1 in
let y = x + 1 in
let z = x + y in z 

Поместите круглые скобки, чтобы прояснить, что это означает:

let x = 1 in (let y = x + 1 in (let z = x + y in (z)))

Итак, вы видите, состояние моделируется последовательностью чистых выражений, которые связывают свободные переменные следующих выражения.

18
ответ дан 23 November 2019 в 02:59
поделиться

Обратите внимание, что утверждение, что функциональное программирование не имеет «состояния», немного вводит в заблуждение и может быть причиной путаницы. У него определенно нет «изменяемого состояния», но он все еще может иметь значения, которыми можно управлять; они просто не могут быть изменены на месте (например, вы должны создать новые значения из старых значений).

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

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

Приложение:

28
ответ дан 23 November 2019 в 02:59
поделиться

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

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

2
ответ дан 23 November 2019 в 02:59
поделиться

In addition to the great answers others are giving, think about the classes Integer and String in Java. Instances of these classes are immutable, but that doesn't make the classes useless just because their instances cannot be changed. The immutability gives you some safety. You know if you use a String or Integer instance as the key to a Map, the key cannot be changed. Compare this to the Date class in Java:

Date date = new Date();
mymap.put(date, date.toString());
// Some time later:
date.setTime(new Date().getTime());

You have silently changed a key in your map! Working with immutable objects, such as in Functional Programming, is a lot cleaner. It's easier to reason about what side effects occur -- none! This means it's easier for the programmer, and also easier for the optimizer.

5
ответ дан 23 November 2019 в 02:59
поделиться

Это просто разные способы сделать одно и то же.

Рассмотрим простой пример, такой как сложение чисел 3, 5 и 10. Представьте, что вы думаете об этом, сначала изменив значение 3, добавив к нему 5, затем прибавив 10 к этой «3», а затем вывести текущее значение «3» (18). Это кажется откровенно смешным, но, по сути, именно так часто осуществляется императивное программирование на основе состояний. В самом деле, у вас может быть много разных «3» со значением 3, но они разные. Все это кажется странным, потому что мы так укоренились в чрезвычайно разумной идее неизменности чисел.

Теперь подумайте о сложении 3, 5 и 10, когда вы принимаете значения неизменными. Вы складываете 3 и 5, чтобы получить другое значение, 8, затем вы добавляете 10 к этому значению, чтобы получить еще одно значение, 18.

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

11
ответ дан 23 November 2019 в 02:59
поделиться

Краткий ответ: вы не можете.

Так в чем же тогда суета о неизменности?

Если вы хорошо разбираетесь в императивном языке, то вы знаете, что «глобальные переменные - это плохой". Зачем? Потому что они вводят (или могут ввести) некоторые очень трудноразрешимые зависимости в вашем коде. И зависимости - это нехорошо; вы хотите, чтобы ваш код был модульным . Части программы как можно меньше влияют на другие части. И FP подводит вас к святому Граалю модульности: никаких побочных эффектов вообще . У вас просто есть f (x) = y. Вставьте x, получите y. Никаких изменений в x или чем-либо еще. ФП заставляет вас перестать думать о состоянии и начать думать о ценностях. Все ваши функции просто получают значения и создают новые значения.

Это дает несколько преимуществ.

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

Во-вторых, это делает программу тривиально распараллеливаемой (эффективное распараллеливание - другое дело).

В-третьих, есть некоторые возможные преимущества в производительности . Допустим, у вас есть функция:

double x = 2 * x

Теперь вы вводите значение 3 на входе и получаете значение 6 на выходе. Каждый раз. Но вы также можете сделать это в обязательном порядке, верно? Ага. Но проблема в том, что в обязательном порядке вы можете сделать даже больше . Я могу сделать:

int y = 2;
int double(x){ return x * y; }

, но я также могу сделать

int y = 2;
int double(x){ return x * (y++); }

Императивный компилятор не знает, будут ли у меня побочные эффекты или нет, что затрудняет оптимизацию (т. Е. Двойное 2 не должно быть 4 каждый раз). Функциональный знает, что я не буду - следовательно, он может оптимизировать каждый раз, когда видит "двойное 2".

Теперь, хотя создание новых значений каждый раз кажется невероятно расточительным для сложных типов значений с точки зрения компьютерной памяти, это не должно быть так. Потому что, если у вас есть f (x) = y, а значения x и y «в основном одинаковы» (например, деревья, которые отличаются только несколькими листами), тогда x и y могут совместно использовать части памяти - потому что ни один из них не будет мутировать .

Итак, если эта неизменяемая вещь так хороша, почему я ответил, что вы не можете сделать ничего полезного без изменяемого состояния. Что ж, без изменчивости вся ваша программа была бы гигантской функцией f (x) = y. То же самое относится ко всем частям вашей программы: только к функциям, причем в «чистом» смысле. Как я уже сказал, это означает, что f (x) = y каждый раз. Так, например, readFile ("myFile.txt" ) должен каждый раз возвращать одно и то же строковое значение. Не очень полезно.

Следовательно, каждый FP предоставляет некоторые средства изменения состояния. «Чистые» функциональные языки (например, Haskell) делают это, используя несколько пугающие концепции, такие как монады, в то время как «нечистые» (например, ML) допускают это напрямую.

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

74
ответ дан 23 November 2019 в 02:59
поделиться

Имейте в виду: функциональные языки полны по Тьюрингу. Следовательно, любую полезную задачу, которую вы выполняете на империтивном языке, можно выполнить на функциональном языке. В конце концов, я думаю, что есть что сказать о гибридном подходе. Такие языки, как F # и Clojure (и я уверен, что другие) поощряют проектирование без сохранения состояния, но при необходимости допускают изменяемость.

2
ответ дан 23 November 2019 в 02:59
поделиться

На самом деле довольно легко иметь что-то, что выглядит как изменяемое состояние даже в языках без изменяемого состояния.

Рассмотрим функцию с типом s -> (a, s) . В переводе с синтаксиса Haskell это означает функцию, которая принимает один параметр типа « s » и возвращает пару значений типов « a » и « s ] ". Если s является типом нашего состояния, эта функция принимает одно состояние и возвращает новое состояние и, возможно, значение (вы всегда можете вернуть «unit», также известное как () , которое является эквивалент типа « void » в C / C ++, как тип « a »). Если вы связываете несколько вызовов функций с типами, подобными этому (получение состояния, возвращаемого одной функцией, и передача его следующей), у вас есть «изменяемое» состояние (фактически вы находитесь в каждой функции, создавая новое состояние и отказываясь от старого.

Будет легче понять, если вы представите изменяемое состояние как «пространство», в котором выполняется ваша программа, а затем подумаете о временном измерении. В момент t1 «пространство» находится в определенном состоянии (скажем, например, некоторая ячейка памяти имеет значение 5). В более поздний момент t2 он находится в другом состоянии (например, эта ячейка памяти теперь имеет значение 10). Каждый из этих временных «срезов» является состоянием, и оно неизменяемо (вы не можете вернуться назад во времени, чтобы изменить их). Итак, с этой точки зрения, вы перешли от полного пространства-времени со стрелкой времени (ваше изменяемое состояние) к набору срезов пространства-времени (несколько неизменных состояний), и ваша программа просто обрабатывает каждый срез как значение и вычисляет каждый из них как функцию, применяемую к предыдущий.

Хорошо, может быть, это было не легче понять: -)

Может показаться бесполезным явное представление всего состояния программы в виде значения, которое должно быть создано только для того, чтобы быть отброшено в следующий момент (просто после создания нового). Для некоторых алгоритмов это может быть естественным, но когда это не так, есть еще одна хитрость. Вместо реального состояния вы можете использовать поддельное состояние, которое является не чем иным, как маркером (назовем тип этого поддельного состояния State # ). Это поддельное состояние существует с точки зрения языка и передается, как и любое другое значение, но компилятор полностью пропускает его при генерации машинного кода. Он служит только для обозначения последовательности выполнения.

В качестве примера предположим, что компилятор дает нам следующие функции:

readRef :: Ref a -> State# -> (a, State#)
writeRef :: Ref a -> a -> State# -> (a, State#)

При переводе этих объявлений, подобных Haskell, readRef получает что-то похожее на указатель или дескриптор значения типа « a » и ложного состояния, и возвращает значение типа « a », на которое указывает первый параметр и новое поддельное состояние. writeRef аналогичен, но вместо этого изменяет указанное значение.

Если вы вызываете readRef , а затем передаете ему поддельное состояние, возвращаемое writeRef (возможно, с другие вызовы несвязанных функций в середине; эти значения состояния создают «цепочку» вызовов функций), он вернет записанное значение. Вы можете снова вызвать writeRef с тем же указателем / дескриптором, и он будет записывать в ту же ячейку памяти, но, поскольку концептуально он возвращает новое (поддельное) состояние, (поддельное) состояние по-прежнему является неизменяемым ( "создан" новый). Компилятор будет вызывать функции в том порядке, в котором он должен был бы их вызывать, если бы существовала реальная переменная состояния, которую нужно было вычислить, но единственное состояние, которое есть, - это полное (изменяемое) состояние реального оборудования.

(Те, кто знаком с Haskell, заметят, что я значительно упростил некоторые вещи и упустил несколько важных деталей. Для тех, кто хочет увидеть больше деталей, взгляните на Control.Monad.State из mtl ], а также в монадах ST и IO (также известных как ST RealWorld ). )

Вы можете задаться вопросом, зачем делать это таким окольным путем (вместо того, чтобы просто иметь изменяемое состояние в языке). Настоящее преимущество состоит в том, что вы обновили состояние вашей программы. То, что раньше было неявным (состояние вашей программы было глобальным, допускало такие вещи, как действие на расстоянии ), теперь стало явным. Функции, которые не получают и не возвращают состояние, не могут его изменять или влиять на него; они «чистые». Более того, у вас могут быть отдельные потоки состояний, и немного магии типов их можно использовать для встраивания императивных вычислений в чистые, не делая их нечистыми (монада ST в Haskell - это обычно используется для этого трюка; State # , о котором я упоминал выше, на самом деле является GHC State # s ,

7
ответ дан 23 November 2019 в 02:59
поделиться

Вот , как вы пишете код без изменяемого состояния : вместо того, чтобы помещать изменяемое состояние в изменяемые переменные, вы помещаете его в параметры функций. И вместо того, чтобы писать циклы, вы пишете рекурсивные функции. Так, например, этот императивный код:

f_imperative(y) {
  local x;
  x := e;
  while p(x, y) do
    x := g(x, y)
  return h(x, y)
}

становится следующим функциональным кодом (синтаксис, подобный схеме):

(define (f-functional y) 
  (letrec (
     (f-helper (lambda (x y)
                  (if (p x y) 
                     (f-helper (g x y) y)
                     (h x y)))))
     (f-helper e y)))

или этот код Haskellish

f_fun y = h x_final y
   where x_initial = e
         x_final   = loop x_initial
         loop x = if p x y then loop (g x y) else x

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

12
ответ дан 23 November 2019 в 02:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: