Вид выбора в функциональном Scala

Лично я думаю, что запутывание - путь. Это просто и может быть эффективно, особенно если весь ваш код находится внутри exe-файла (я не уверен, что беспокоит «испортить IL»).

Однако, если вы чувствуете, что это не сработает для вас, возможно, вы сможете зашифровать свой exe-файл и встроить его в качестве ресурса в панель запуска. Самый простой способ справиться с этим - расшифровать ресурс exe, записать его в файл и выполнить. Как только исполняемый файл завершится, удалите файл. Вы также можете запустить его через функции Emit. Я понятия не имею, как это будет работать, но вот статья для начала - Использование Reflection Emit для кэширования сборок .NET .

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

7
задан Lytol 4 November 2009 в 06:54
поделиться

5 ответов

As starblue already said, you need a function that calculates the minimum of a list and returns the list with that element removed. Here is my tail recursive implementation of something similar (as I believe foldl is tail recursive in the standard library), and I tried to make it as functional as possible :). It returns a list that contains all the elements of the original list (but kindof reversed - see the explanation below) with the minimum as a head.

def minimum(xs: List[Int]): List[Int] =
  (List(xs.head) /: xs.tail) {
    (ys, x) =>
      if(x < ys.head) (x :: ys)
      else            (ys.head :: x :: ys.tail)
  }

This basically does a fold, starting with a list containing of the first element of xs If the first element of xs is smaller than the head of that list, we pre-append it to the list ys. Otherwise, we add it to the list ys as the second element. And so on recursively, we've folded our list into a new list containing the minimum element as a head and a list containing all the elements of xs (not necessarily in the same order) with the minimum removed, as a tail. Note that this function does not remove duplicates.

After creating this helper function, it's now easy to implement selection sort.

def selectionSort(xs: List[Int]): List[Int] =  
  if(xs.isEmpty) List()
  else {
    val ys = minimum(xs)
    if(ys.tail.isEmpty) 
      ys
    else
      ys.head :: selectionSort(ys.tail)
  }

Unfortunately this implementation is not tail recursive, so it will blow up the stack for large lists. Anyway, you shouldn't use a O(n^2) sort for large lists, but still... it would be nice if the implementation was tail recursive. I'll try to think of something... I think it will look like the implementation of a fold.

Tail Recursive!

To make it tail recursive, I use quite a common pattern in functional programming - an accumulator. It works a bit backward, as now I need a function called maximum, which basically does the same as minimum, but with the maximum element - its implementation is exact as minimum, but using > instead of <.

def selectionSort(xs: List[Int]) = {
  def selectionSortHelper(xs: List[Int], accumulator: List[Int]): List[Int] =
    if(xs.isEmpty) accumulator
    else {
          val ys = maximum(xs)
          selectionSortHelper(ys.tail, ys.head :: accumulator)
    }

  selectionSortHelper(xs, Nil) 
  }

EDIT: Changed the answer to have the helper function as a subfunction of the selection sort function.

It basically accumulates the maxima to a list, which it eventually returns as the base case. You can also see that it is tail recursive by replacing accumulator by throw new NullPointerException - and then inspect the stack trace.

Here's a step by step sorting using an accumulator. The left hand side shows the list xs while the right hand side shows the accumulator. The maximum is indicated at each step by a star.

64* 25 12 22 11  ------- Nil
11 22 12 25*     ------- 64
22* 12 11        ------- 25 64
11 12*           ------- 22 25 64
11*              ------- 12 22 25 64
Nil              ------- 11 12 22 25 64

The following shows a step by step folding to calculate the maximum:

maximum(25 12 64 22 11)

25 :: Nil         /: 12 64 22 11  -- 25 > 12, so it stays as head
25 :: 12          /: 64 22 11     -- same as above
64 :: 25 12       /: 22 11        -- 25 < 64, so the new head is 64
64 :: 22 25 12    /: 11           -- and stays so
64 :: 11 22 25 12 /: Nil          -- until the end

64 11 22 25 12
10
ответ дан 6 December 2019 в 10:01
поделиться

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

1
ответ дан 6 December 2019 в 10:01
поделиться

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

Это поможет избавиться от некоторых переменных.

Это был, наверное, самый сложный урок для меня - пытаться больше двигаться в сторону FP.

Я не решаюсь показать здесь решения, так как думаю, что вам лучше сначала попробовать.

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

0
ответ дан 6 December 2019 в 10:01
поделиться

У вас должны возникнуть проблемы при выполнении сортировки по выбору в функциональном стиле, так как это алгоритм сортировки на месте. На месте по определению не работает.

Основная проблема, с которой вы столкнетесь, заключается в том, что вы не можете менять местами элементы. Вот почему это важно. Предположим, у меня есть список (a 0 ... a x ... a n ), где a x - минимальное ценность. Вам нужно получить x , а затем составить список (a 0 ... a x-1 a x + 1 a n ). Проблема в том, что вам обязательно придется скопировать элементы a 0 в x-1 , если вы хотите сохранить чисто функциональные возможности. Другие функциональные структуры данных, особенно деревья, могут иметь лучшую производительность, чем эта.

2
ответ дан 6 December 2019 в 10:01
поделиться

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

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

define bubble(data)
  if data is empty or just one element: return data;
  otherwise, if the first element < the second,
    return first element :: bubble(rest of data);
    otherwise, return second element :: bubble(
      first element :: (rest of data starting at 3rd element)).

После завершения рекурсии самый большой элемент оказывается в конце списка. Теперь,

define bubblesort [data]
  apply bubble to data as often as there are elements in data.

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

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

Я бы порекомендовал реализовать сортировку слиянием:

Break list into two sub-lists, 
either by counting off half the elements into one sublist 
  and the rest in the other,
or by copying every other element from the original list 
  into either of the new lists.

Sort each of the two smaller lists (recursion here, obviously).

Assemble a new list by selecting the smaller from the front of either sub-list
until you've exhausted both sub-lists.

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

Удачи, удачи!

1
ответ дан 6 December 2019 в 10:01
поделиться
Другие вопросы по тегам:

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