Вы находите тихую необходимость в переменных, которые можно заменить, и раз так почему?

Попробуйте использовать триггер после UPDATE:

    CREATE TRIGGER ModDate
    ON YourTable
    AFTER UPDATE
AS
BEGIN        
    INSERT INTO dbo.LogTable(DateModified)
    VALUES (GETDATE());
    PRINT 'New Block Date Added';
END 

ОБНОВЛЕНИЕ:

Когда триггер работает, под капотом есть две таблицы:

  • «ВСТАВЛЕНО»
  • «УДАЛЕНО»

Данные в этих виртуальных таблицах зависят от того, что вы делаете:

  • Операция вставки : при вставке записи виртуальная таблица «вставленная» содержит вновь вставленную запись, где виртуальная таблица «УДАЛЕНО» остается пустой
  • Операция обновления: при обновлении записи сначала старая запись будет помещаться в виртуальную таблицу «DELETED», а вновь обновленная запись удерживается виртуальной таблицей «INSERTED».

    Это означает, что вы можете получить старое значение из «DELETED» и текущее обновляемое значение через виртуальную таблицу «INSERTED». Вы можете запросить их как:

    -- To get the old record value
    SELECT * FROM DELETED
    
    -- To get the updated value
    SELECT * FROM INSERTED
    
  • Операция удаления: при попытке удалить какую-либо конкретную запись удаленная запись будет вставлена ​​в виртуальную таблицу «УДАЛЕНО».

    [118 ]

Итак, ваш триггер должен выглядеть так:

CREATE TRIGGER ModDate
    ON YourTable
    AFTER UPDATE
AS
BEGIN        
    INSERT INTO dbo.LogTable(DateModified, IdUpdatedRow)
    SELECT GETDATE(), INSERTED.YourIdOfUpdatedRow;
    PRINT 'New Block Date Added';
END 
33
задан Community 23 May 2017 в 10:27
поделиться

14 ответов

Я никогда не определял такой случай. И в то время как можно всегда просто изобретать новые имена, как в преобразовании в форму SSA, я на самом деле нахожу, что это легко и естественно для каждого значения иметь свое собственное имя. Язык как Haskell дает мне большой выбор относительно который значения назвать, и два различных места для помещения привязки имени (let и where). Я нахожу, что однократное присвоение формируется довольно естественный и нисколько не трудный.

я действительно иногда пропускаю способность иметь указатели на изменяемые объекты на "куче". Но эти вещи не имеют никаких имен, таким образом, это не то же возражение. (И я также нахожу, что, когда я использую изменяемые объекты на "куче", я склонен писать больше ошибок!)

11
ответ дан 27 November 2019 в 18:12
поделиться

Я действительно не думал об этом очень кроме того, теперь, когда Вы указываете на это.

На самом деле я пытаюсь не использовать несколько присвоений подсознательно.

Вот пример того, что я говорю о в Python

start = self.offset%n
if start:
    start = n-start

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

Я не скучал бы по нескольким присвоение вообще.

0
ответ дан 27 November 2019 в 18:12
поделиться

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

0
ответ дан 27 November 2019 в 18:12
поделиться

Если у Вас есть функция, которая создает ленивый список/дерево, тогда уменьшает его снова, функциональный компилятор может быть в состоянии оптимизировать его с помощью вырубка леса .

, Если это хитро, это не могло бы. Тогда Вы - вид неудачливых, производительность & мудрая память, если Вы не можете выполнить итерации и использовать изменяемую переменную.

1
ответ дан 27 November 2019 в 18:12
поделиться

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

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

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

Часто параллелизм брошен в аргумент окружающая неизменность. Я работал над проблемными наборами, которые потребовали 100 + центральные процессоры решать в разумный срок. И это преподавало мне одну очень важную вещь: Большую часть времени параллелизация управления графиками данных является действительно не видом вещи, которая будет самым эффективным способом параллелизировать. Это уверенный может извлечь выгоду значительно, но неустойчивость является настоящей проблемой в том пространстве задач. Так обычно работающий над несколькими изменяемыми графиками в параллели и обменивающийся информацией с неизменными сообщениями более эффективный путь. Что означает, когда я знаю, что график изолируется, что я не показал его к внешнему миру, я хотел бы выполнить свои операции на нем самым кратким способом, о котором я могу думать. И это обычно включает видоизменение данных. Но после них операция на данных я хочу открыть данные до целого мира - и в этом суть где я обычно становлюсь немного возбужденным, если данные изменяемы. Поскольку другие части программы могли повредить данные, состояние становится недопустимым... потому что после открытия до мира данные часто входят в мир параллелизма.

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

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

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

0
ответ дан 27 November 2019 в 18:12
поделиться

Локальный (метод) переменным, конечно, никогда нельзя присваивать дважды. Но даже в функциональном программировании, повторно присваивающем переменную, позволяется. Это изменяет (часть) значение, которое это не позволяется. И как dsimcha уже ответил, для очень больших структур (возможно, в корне приложения) не кажется выполнимым мне заменить всю структуру.Подумайте об этом. Состояние приложения все содержится в конечном счете entrypoint методом Вашего приложения. Если бы абсолютно никакое состояние не может измениться без того, чтобы быть замененным, необходимо было бы перезапустить приложение с каждым нажатием клавиши.:(

1
ответ дан 27 November 2019 в 18:12
поделиться

В ответ на Jason -

function forbidden_input?(s)
    (s = '?' and not administration.qmark_ok) ||
    (s = '*' and not administration.stat_ok)  ||
    (s = '0' and not 'root node visible' in system.permissions_for(current_user))

n = if forbidden_input?(s)
    fail "'" + s + "' is not allowed."
  else
    case s
      /^\d*$/ : s.to_int
      ''      : 0
      '*'     : a.length
      '?'     : a.length.random
      else    fail "I don't know how many you want"
3
ответ дан 27 November 2019 в 18:12
поделиться

Благодаря тезису Черча-Тьюринга мы знаем, что что-либо, что может быть записано на полном по Тьюрингу языке, может быть записано в любой полный по Тьюрингу язык. Так, когда Вы разбираетесь вниз к нему, нет ничего, что Вы не можете сделать в Lisp, который Вы не могли сделать в C#, если бы Вы достаточно старались, или наоборот. (Главное, любой собирается быть скомпилированным вниз в x86 машинный язык в большинстве случаев так или иначе.)

Так, ответ на Ваш вопрос: нет таких случаев. Все там, случаи, которые легче для людей постигать в одной парадигме/языке, или другой - и простота понимания здесь связывается с обучением и опытом.

1
ответ дан 27 November 2019 в 18:12
поделиться

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

 shuffle(Lst) ->
     array:to_list(shuffle(array:from_list(Lst), erlang:length(Lst) - 1)).

 shuffle(Array, 0) -> Array;
 shuffle(Array, N) ->
     K = random:uniform(N) - 1,
     Ek = array:get(K, Array),
     En = array:get(N, Array),
     shuffle(array:set(K, En, array:set(N, Ek, Array)), N-1).

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

Вы не получите ответ на этот вопрос, потому что никакие примеры не существуют. Это - только вопрос знакомства с этим стилем.

4
ответ дан 27 November 2019 в 18:12
поделиться

Я думаю, что Вы найдете, что самые продуктивные языки позволяют Вам смешивать функциональные и обязательные стили, такие как OCaml и F#.

В большинстве случаев, я могу написать код, который является просто длинной линией "карты x к y, уменьшите y до z". В 95% случаев функциональное программирование упрощает мой код, но существует одна область, где неизменность показывает свои зубы:

широкая несоизмеримость между простотой реализации и неизменным стеком и неизменной очередью.

Стеки легки и сцепляются хорошо с персистентностью, очереди смешны.

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

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

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

<час>

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

module Vector =
    type point =
        { x : float; y : float}
        with
            static member ( + ) ((p1 : point), (p2 : point)) =
                { x = p1.x + p2.x;
                  y = p1.y + p2.y;}

            static member ( * ) ((p : point), (scalar : float)) =
                { x = p.x * scalar;
                  y = p.y * scalar;}

            static member ( - ) ((p1 : point), (p2 : point)) = 
                { x = p1.x - p2.x;
                  y = p1.y - p2.y;}

    let empty = { x = 0.; y = 0.;}
    let to_tuple2 (p : point) = (p.x, p.y)
    let from_tuple2 (x, y) = { x = x; y = y;}
    let crossproduct (p1 : point) (p2 : point) =
        { x = p1.x * p2.y; y = -p1.y * p2.x }

Мы можем определить нашу функцию области, использующую определенное волшебство кортежа:

let area (figure : point list) =
    figure
    |> Seq.map to_tuple2
    |> Seq.fold
        (fun (sum, (a, b)) (c, d) -> (sum + a*d - b*c, (c, d) ) )
        (0., to_tuple2 (List.hd figure))
    |> fun (sum, _) -> abs(sum) / 2.0

Или мы можем использовать векторное произведение вместо этого

let area2 (figure : point list) =
    figure
    |> Seq.fold
        (fun (acc, prev) cur -> (acc + (crossproduct prev cur), cur))
        (empty, List.hd figure)
    |> fun (acc, _) -> abs(acc.x + acc.y) / 2.0

, я не нахожу ни одну функцию нечитабельной.

6
ответ дан 27 November 2019 в 18:12
поделиться

Самая трудная проблема, с которой я столкнулся, переставляет список. Фишер-Йетс алгоритм (также иногда известный как алгоритм Knuth) включает итерацию через список, подкачивающий каждый объект со случайным другим объектом. Алгоритм является O (n), известный и давно доказанный корректный (важное свойство в некоторых приложениях). Но это требует изменяемых массивов.

, Который не должен говорить, Вы не можете сделать перестановки в функциональной программе. Oleg Kiselyov имеет записанный об этом . Но если я понимаю его правильно, функциональная перестановка является O (n., регистрируют n), потому что это работает путем создания двоичного дерева.

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

-- | Implementation of the random swap algorithm for shuffling.  Reads a list
-- into a mutable ST array, shuffles it in place, and reads out the result
-- as a list.

module Data.Shuffle (shuffle) where


import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.STRef
import System.Random

-- | Shuffle a value based on a random seed.
shuffle :: (RandomGen g) => g -> [a] -> [a]
shuffle _ [] = []
shuffle g xs = 
    runST $ do
      sg <- newSTRef g
      let n = length xs
      v <- newListArray (1, n) xs
      mapM_ (shuffle1 sg v) [1..n]
      getElems v

-- Internal function to swap element i with a random element at or above it.
shuffle1 :: (RandomGen g) => STRef s g -> STArray s Int a -> Int -> ST s ()
shuffle1 sg v i = do
  (_, n) <- getBounds v
  r <- getRnd sg $ randomR (i, n)
  when (r /= i) $ do
    vi <- readArray v i
    vr <- readArray v r
    writeArray v i vr
    writeArray v r vi


-- Internal function for using random numbers
getRnd :: (RandomGen g) => STRef s g -> (g -> (a, g)) -> ST s a
getRnd sg f = do
  g1 <- readSTRef sg
  let (v, g2) = f g1
  writeSTRef sg g2
  return 

v

16
ответ дан 27 November 2019 в 18:12
поделиться

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

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

  1. обычно требуется больше операторов (или больше строк кода) для написания кода этот путь. Краткость имеет значение.
  2. Это почти всегда менее эффективно. Да я знаю, что Вы говорите о более высоких языках - обзоре ярмарки - но даже в мире Java и C#, далеко от блока, вопросов скорости. Существует немного приложений, где скорость не важна.
  3. не столь легко понять. Хотя SSA "более прост" в математическом смысле, это более абстрактно от здравого смысла, который является тем, что имеет значение в реальном программировании. Если необходимо быть действительно умными к grok это, то это не имеет никакого места в программировании в целом.

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

Это - более реальный пример с ответвлениями и условиями. Вы могли записать это как единственный "оператор?" Если так, Ваш "оператор" действительно отличается от многих отдельных операторов? В противном случае, в каком количестве временных переменных только для записи Вы нуждаетесь? И то, что ситуация значительно лучше, чем просто наличие единственной переменной?

15
ответ дан 27 November 2019 в 18:12
поделиться

Я пропустил бы присвоения на нечисто функциональном языке. Главным образом, потому что они препятствуют полноценности циклов. Примеры (Scala):

def quant[A](x : List[A], q : A) = {
  var tmp : A=0
  for (el <- x) { tmp+= el; if(tmp > q) return el; }
  // throw exception here, there is no prefix of the list with sum > q
}

Это должно вычислить квантиль списка, отметить аккумулятор tmp которому присваивают многократно.

Подобный пример был бы:

def area(figure : List[Point]) : Float = {
  if(figure.empty) return 0
  val last = figure(0)
  var first= figure(0)
  val ret = 0
  for (pt <- figure) {
    ret+=crossprod(last - first, pt - first)
    last = pt
  }
  ret
}

Отметьте главным образом last переменная.

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

3
ответ дан 27 November 2019 в 18:12
поделиться

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

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

Работа в Haskell - действительно хороший способ исследовать необходимость изменяемых переменных, поскольку по умолчанию неизменяемы, но доступны изменяемые (как IORefs , MVars и т. Д. ). Я недавно, эм, сам "исследовал" таким образом, и пришел к следующим выводам.

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

    1. 1213] Для межпоточного взаимодействия изменяемые переменные необходимы по довольно очевидным причинам. (Это характерно для Haskell; системы времени выполнения, которые используют передачу сообщений на самом низком уровне, конечно, не нуждаются в них.) Однако такое использование достаточно редко, так что приходится использовать функции для их чтения и записи ( readIORef fooRef val и т. д.) не является проблемой.

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

    Итак, в эти дни я довольно твердо встал на сторону неизменяемых переменных.

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

1
ответ дан 27 November 2019 в 18:12
поделиться
Другие вопросы по тегам:

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