使用函数式编程的唯一随机数组

时间:2016-01-16 04:50:05

标签: functional-programming

我试图在功能范例中编写一些代码来进行练习。有一种情况我头脑中有一些问题。我试图从1,100创建一个由5个唯一整数组成的数组。我已经能够在不使用函数式编程的情况下解决这个问题:

let uniqueArray = [];

while (uniqueArray.length< 5) {
  const newNumber = getRandom1to100();
  if (uniqueArray.indexOf(newNumber) < 0) {
    uniqueArray.push(newNumber)
  }
}

我可以访问lodash,因此我可以使用它。我在思考:

const uniqueArray = [
  getRandom1to100(), 
  getRandom1to100(), 
  getRandom1to100(), 
  getRandom1to100(), 
  getRandom1to100()
].map((currentVal, index, array) => {
      return array.indexOf(currentVal) > -1 ? getRandom1to100 : currentVal;
    });

但是这显然不会起作用,因为它总会返回true,因为索引将在数组中(我可以删除更多的工作)但更重要的是它没有检查第二次所有值都是唯一的。但是,我不太确定如何功能模拟while循环。

4 个答案:

答案 0 :(得分:2)

这是OCaml中的一个例子,关键是你使用累加器和递归。

let make () =
  Random.self_init ();
  let rec make_list prev current max accum =
    let number = Random.int 100 in
    if current = max then accum
    else begin
      if number <> prev
      then (number + prev) :: make_list number (current + 1) max accum
      else accum
    end
  in
  make_list 0 0 5 [] |> Array.of_list

这不能保证数组是唯一的,因为它只能通过前一个检查。您可以通过在makemake_list之间的闭包中隐藏哈希表并进行恒定时间查找来解决此问题。

答案 1 :(得分:1)

这是一种基于流的Python方法。

Python的懒惰流版本是生成器。它们可以通过各种方式生成,包括看起来像函数定义但使用关键字yield而不是return的东西。例如:

import random

def randNums(a,b):
    while True:
        yield random.randint(a,b)

通常,生成器用于for循环,但是最后一个生成器具有无限循环,因此如果您尝试迭代它,它将挂起。相反,您可以使用内置函数next()来获取字符串中的下一个项目。编写一个像Haskell take

这样的函数是很方便的
def take(n,stream):
    items = []
    for i in range(n):
        try:
            items.append(next(stream))
        except StopIteration:
            return items
    return items

在Python中StopIteration在生成器耗尽时引发。如果这发生在n个项目之前,那么这个代码只会返回但是生成了很多,所以也许我应该将它称为takeAtMost。如果您放弃了错误处理,那么如果没有足够的项目(可能是您想要的话)它会崩溃。无论如何,这用作:

>>> s = randNums(1,10)
>>> take(5,s)
[6, 6, 8, 7, 2]

当然,这允许重复。

为了使事物独特(并以功能方式这样做),我们可以编写一个函数,它将流作为输入并返回由唯一项组成的流作为输出:

def unique(stream):
    def f(s):
        items = set()
        while True:
            try:
                x = next(s)
                if not x in items:
                    items.add(x)
                    yield x
            except StopIteration:
                raise StopIteration
    return f(stream)

这会在闭包中创建一个流,其中包含一个可以跟踪已看到的项目的集合,只会产生唯一的项目。在这里,我传递任何StopIteration异常。如果底层生成器没有更多元素,则不再有唯一元素。我不是100%确定我是否需要明确传递异常 - (它可能会自动发生)但是看起来很干净。

像这样使用:

>>> take(5,unique(randNums(1,10)))
[7, 2, 5, 1, 6]

take(10,unique(randNums(1,10)))将产生1-10的随机排列。 take(11,unique(randNums(1,10)))永远不会终止。

答案 2 :(得分:0)

这是一个非常好的问题。它实际上很常见。它甚至有时被问到是一个面试问题。

这是我从0到100生成5个整数的解决方案。

let rec take lst n =
  if n = 0 then []
  else
    match lst with
    | [] -> []
    | x :: xs -> x :: take xs (n-1)

let shuffle d =
  let nd = List.map (fun c -> (Random.bits (), c)) d in
  let sond = List.sort compare nd in
  List.map snd sond

let rec range a b =
  if a >= b then []
  else a :: range (a+1) b;;

let _ = 
    print_endline 
        (String.concat "\t" ("5 random integers:" :: List.map string_of_int (take (shuffle (range 0 101)) 5)))

答案 3 :(得分:-1)

这是怎么回事:

const addUnique = (ar) => {
    const el = getRandom1to100();
    return ar.includes(el) ? ar : ar.concat([el])
}

const uniqueArray = (numberOfElements, baseArray) => {
    if (numberOfElements < baseArray.length) throw 'invalid input' 
    return baseArray.length === numberOfElements ? baseArray : uniqueArray(numberOfElements, addUnique(baseArray))
}
const myArray = uniqueArray(5, [])