Фильтрация массива с несколькими поисками значения ключа в PHP

Я использую плагин wordpress для расширенного проектирования / логики / обработки форм, и одним из его (самых больших) недостатков является неудобный способ предоставления данных формы. Данные приведены в массиве с двумя или тремя элементами для каждого поля.

если это скрытое поле:

  1. $$$ n => id - где n - поле ' s порядок в форме, а NAME - это имя / идентификатор.
  2. id => value - где id - это имя / id, а значение - это значение.

Если это видимое поле:

  1. $$$ n => LABEL - где n - порядок поля в форме, а LABEL - удобочитаемая метка.
  2. $$$ id => LABEL - где id - имя / id поля
  3. LABEL => value - где значение - это то, что я на самом деле хочу.

Я пытаюсь написать функцию, которая будет использовать этот ужасный массив и вернет более простой с одним ] id => value pair для каждого поля.

Например, он примет это (порядок полей не может быть гарантирован):

array(
    '$$$1' => 'command',
    'command' => 'signup',
    '$$$2' => 'First Name',
    '$$$firstname' => 'First Name',
    'First Name' => 'John',
    '$$$3' => 'Email Address',
    '$$$email' => 'Email Address',
    'Email Address' => 'john@example.com'
);

и вернет:

array(
    'command' => 'signup',
    'email' => 'john@example.com',
    'firstname' => 'John'
);

Этот код работает, но чувствует себя очень неловко. Можете ли вы помочь уточнить это? Спасибо! должна быть разница между «throw e»; и "бросай;" Но из: ...

ссылаясь на большое количество документации в сети, в частности, на SO, например: Как правильно перезапустить исключение в C #? должна быть разница между «throw e»; и "throw;".

Но, из: http://bartdesmet.net/blogs/bart/archive/2006/03/12/3815.aspx ,

этот код:

using System;

class Ex
{
   public static void Main()
  {
  //
  // First test rethrowing the caught exception variable.
  //
  Console.WriteLine("First test");
  try
  {
     ThrowWithVariable();
  }
  catch (Exception ex)
  {
     Console.WriteLine(ex.StackTrace);
  }

  //
  // Second test performing a blind rethrow.
  //
  Console.WriteLine("Second test");
  try
  {
     ThrowWithoutVariable();
  }
  catch (Exception ex)
  {
     Console.WriteLine(ex.StackTrace);
  }
}

 private static void BadGuy()
 {
   //
   // Some nasty behavior.
  //
   throw new Exception();
 }

   private static void ThrowWithVariable()
 {
   try
   {
         BadGuy();
   }
  catch (Exception ex)
  {
     throw ex;
  }
}

   private static void ThrowWithoutVariable()
{
  try
  {
     BadGuy();
  }
  catch
  {
     throw;
  }
   }
}

дает следующий результат:

$ /cygdrive/c/Windows/Microsoft.NET/Framework/v4.0.30319/csc.exe Test.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

$ ./Test.exe
First test
   at Ex.ThrowWithVariable()
   at Ex.Main()
Second test
   at Ex.ThrowWithoutVariable()
   at Ex.Main()

, что полностью противоречит сообщению в блоге.

Такой же результат получается с кодом из: http://crazorsharp.blogspot.com/2009/ 08 / rethrowing-exception-без-resetting.html

Оригинальный вопрос : что я делаю не так?

ОБНОВЛЕНИЕ : тот же результат с .Net 3.5 / csc.exe 3.5.30729.4926

SUMUP : все ваши ответы были великолепны, еще раз спасибо.

Итак, причина заключается в том, что из-за 64-битного JITter эффективно указывается.

Мне пришлось выбрать только один ответ, и вот почему я выбрал LukeH ответ:

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

  • он предоставил флаг NoInlining, который является самым простым способом избежать такого поведения.

Однако теперь эта проблема поднимает другой вопрос. : соответствует ли это поведение всем спецификациям .Net: CLR и языкам программирования C #?

ОБНОВЛЕНИЕ : эта оптимизация кажется совместимой в соответствии с: Throw VS rethrow: тот же результат? (спасибо 0xA3 )

Заранее благодарю за помощь.

10
задан Community 23 May 2017 в 12:07
поделиться

5 ответов

Я не могу воспроизвести проблему - использование .NET 3.5 (32-разрядная версия) дает те же результаты, что и в статье Барта.

Я предполагаю, что компилятор / джиттер .NET 4 - или, может быть, это 64-битный компилятор / джиттер, если это происходит и в версии 3.5 - встраивает метод BadGuy в вызывающие методы. . Попробуйте добавить следующий атрибут MethodImpl в BadGuy и посмотрите, имеет ли это какое-либо значение:

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
private static void BadGuy()
{
    //
    // Some nasty behavior.
    //
    throw new Exception();
}
4
ответ дан 4 December 2019 в 00:59
поделиться

Используйте отладочную сборку, и вы увидите разницу более четко. При отладочной сборке первый запуск покажет местоположение как строку throw ex, а второй - как происходящий из фактического вызова BadGuy. Очевидно, что "проблема" заключается в вызове BadGuy, а не в строке throw ex, и вы будете гоняться за меньшим количеством призраков, используя прямой оператор throw;.

При такой малой глубине стека преимущества не столь очевидны, при очень глубоком стеке вы замаскируете реальный источник проблемы и потеряете некоторую точность, вручную выбрасывая исключение вместо использования встроенного оператора re-throw.

1
ответ дан 4 December 2019 в 00:59
поделиться

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

Я подозреваю, что происходит то, что инлайнинг компилятора просто заменил вызов BadGuy() на throw new Exception();, потому что это единственное утверждение в BadGuy().

Если отключить опцию 'Optimize code' в окне свойств проекта -> Build, то и Release и Debug сборка дают одинаковый результат, который показывает BadGuy() в верхней части трассировки стека.

3
ответ дан 4 December 2019 в 00:59
поделиться

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

    //This terrible hack makes sure track trace is preserved if exception is re-thrown
    internal static Exception AppendStackTrace(Exception ex)
    {
        //Fool CLR into appending stack trace information when the exception is re-thrown
        var remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString",
                                                                 BindingFlags.Instance |
                                                                 BindingFlags.NonPublic);
        if (remoteStackTraceString != null)
            remoteStackTraceString.SetValue(ex, ex.StackTrace + Environment.NewLine);

        return ex;
    }
0
ответ дан 4 December 2019 в 00:59
поделиться

Похоже, JIT-оптимизаторы немного поработали. Как видите, стек вызовов во втором случае отличается от первого, когда вы запускаете сборку Debug. Однако в сборке Release оба стека вызовов идентичны из-за оптимизации.

Чтобы увидеть, что это связано с дрожанием, вы можете украсить методы атрибутом MethodImplAttribute :

[MethodImpl(MethodImplOptions.NoOptimization)]
private static void ThrowWithoutVariable()
{
    try
    {
        BadGuy();
    }
    catch
    {
        throw;
    }
}

Обратите внимание, что IL по-прежнему отличается для ThrowWithoutVariable и . ThrowWithVariable:

.method private hidebysig static void  ThrowWithVariable() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init ([0] class [mscorlib]System.Exception ex)
  .try
  {
    IL_0000:  call       void Ex::BadGuy()
    IL_0005:  leave.s    IL_000a
  }  // end .try
  catch [mscorlib]System.Exception 
  {
    IL_0007:  stloc.0
    IL_0008:  ldloc.0
    IL_0009:  throw
  }  // end handler
  IL_000a:  ret
} // end of method Ex::ThrowWithVariable

.method private hidebysig static void  ThrowWithoutVariable() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .try
  {
    IL_0000:  call       void Ex::BadGuy()
    IL_0005:  leave.s    IL_000a
  }  // end .try
  catch [mscorlib]System.Object 
  {
    IL_0007:  pop
    IL_0008:  rethrow
  }  // end handler
  IL_000a:  ret
} // end of method Ex::ThrowWithoutVariable

Обновите, чтобы ответить на ваш последующий вопрос, соответствует ли это спецификации CLI

На самом деле соответствует, а именно позволяет JIT-компилятору выполнять важные оптимизации. Приложение F гласит на странице 52 (выделено мной):

Некоторые CIL-инструкции выполняют неявные проверки во время выполнения, которые гарантируют память и безопасность типа. Изначально интерфейс командной строки гарантировал, что исключения были точный , что означает, что состояние программы был сохранен, когда было исключение брошенный. Однако обеспечение точного исключения для неявных проверок делает некоторые важные оптимизации практически невозможно применить. Теперь программисты могут объявить через настраиваемый атрибут, что метод «расслабленный», что говорит о том, что исключения возникающие из-за неявных проверок во время выполнения не нужно быть точным.

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

  • Подъем неявных проверок во время выполнения петель.
  • Переупорядочивание итераций цикла (например, векторизация и автоматическая многопоточность)
  • Переключение циклов
  • Встраивание, которое делает встроенный как минимум так же быстро, как эквивалентный макрос
3
ответ дан 4 December 2019 в 00:59
поделиться
Другие вопросы по тегам:

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