我是F#的新手,我正在试图弄清楚如何从列表/字符串数组中返回一个随机字符串值。
我有一个这样的清单:
["win8FF40", "win10Chrome45", "win7IE11"]
如何从上面的列表中随机选择并返回一个项目?
这是我的第一次尝试:
let combos = ["win8FF40";"win10Chrome45";"win7IE11"]
let getrandomitem () =
let rnd = System.Random()
fun (combos : string[]) -> combos.[rnd.Next(combos.Length)]
答案 0 :(得分:13)
此处由 latkin 和 mydogisbox 给出的答案都很好,但我仍然想添加有时使用的第三种方法。这种方法并不快,但它更灵活,更易于组合,并且对于小序列来说足够快。根据您的需要,您可以使用此处给出的更高性能选项之一,或者您可以使用以下选项。
我经常定义一个shuffleR
函数,而不是直接让您选择单个元素:
open System
let shuffleR (r : Random) xs = xs |> Seq.sortBy (fun _ -> r.Next())
此函数的类型为System.Random -> seq<'a> -> seq<'a>
,因此它适用于任何类型的序列:列表,数组,集合和延迟评估的序列(尽管不具有无限序列)。
如果您想要列表中的单个随机元素,您仍然可以这样做:
> [1..100] |> shuffleR (Random ()) |> Seq.head;;
val it : int = 85
但你也可以拿三个随机挑选的元素:
> [1..100] |> shuffleR (Random ()) |> Seq.take 3;;
val it : seq<int> = seq [95; 92; 12]
有时,我并不关心必须传递Random
值,所以我改为定义这个替代版本:
let shuffleG xs = xs |> Seq.sortBy (fun _ -> Guid.NewGuid())
它的工作方式相同:
> [1..100] |> shuffleG |> Seq.head;;
val it : int = 11
> [1..100] |> shuffleG |> Seq.take 3;;
val it : seq<int> = seq [69; 61; 42]
虽然Guid.NewGuid()
的目的并不是提供随机数,但对于我的目的而言,它通常是随机的 - 随机的,在不可预测的的意义上。
shuffleR
和shuffleG
都不是真正随机的。由于Random
和Guid.NewGuid()
的工作方式,这两个函数都可能导致分布略有偏差。如果这是一个问题,您可以定义更通用的shuffle
函数:
let shuffle next xs = xs |> Seq.sortBy (fun _ -> next())
此函数的类型为(unit -> 'a) -> seq<'b> -> seq<'b> when 'a : comparison
。它仍然可以与Random
:
> let r = Random();;
val r : Random
> [1..100] |> shuffle (fun _ -> r.Next()) |> Seq.take 3;;
val it : seq<int> = seq [68; 99; 54]
> [1..100] |> shuffle (fun _ -> r.Next()) |> Seq.take 3;;
val it : seq<int> = seq [99; 63; 11]
但您也可以将它与基类库提供的一些加密安全随机数生成器一起使用:
open System.Security.Cryptography
open System.Collections.Generic
let rng = new RNGCryptoServiceProvider ()
let bytes = Array.zeroCreate<byte> 100
rng.GetBytes bytes
let q = bytes |> Queue
FSI:
> [1..100] |> shuffle (fun _ -> q.Dequeue()) |> Seq.take 3;;
val it : seq<int> = seq [74; 82; 61]
不幸的是,正如您从此代码中看到的那样,它非常麻烦和脆弱。你必须预先知道序列的长度; RNGCryptoServiceProvider
实施IDisposable
,因此您应确保在使用后处置rng
;并且项目将在使用后从q
移除,这意味着它不可重复使用。
相反,如果确实需要加密正确的排序或选择,那么这样做会更容易:
let shuffleCrypto xs =
let a = xs |> Seq.toArray
use rng = new RNGCryptoServiceProvider ()
let bytes = Array.zeroCreate a.Length
rng.GetBytes bytes
Array.zip bytes a |> Array.sortBy fst |> Array.map snd
用法:
> [1..100] |> shuffleCrypto |> Array.head;;
val it : int = 37
> [1..100] |> shuffleCrypto |> Array.take 3;;
val it : int [] = [|35; 67; 36|]
但这并不是我曾经做过的事情,但我认为为了完整起见我会把它包含在这里。虽然我没有测量它,但它很可能不是最快的实现,但它应该是加密随机的。
答案 1 :(得分:8)
您的问题是,您正在混合package pack1;
parameter from_pack = 1;
typedef struct packed
{
logic [7:0] test1;
logic test2;
} test_t;
function automatic void hello();
$display("in pack1");
endfunction
endpackage
package pack2;
parameter from_pack = 2;
typedef struct packed
{
logic [7:0] test1;
logic test2;
logic [18:0] test3;
} test_t;
function automatic void hello();
$display("in pack2");
endfunction
endpackage
module top();
parameter PACKAGE_INST = 1;
generate
case (PACKAGE_INST)
1: begin
// import pack1::hello;
import pack1::*;
end
default: begin
// import pack2::hello;
import pack2::*;
end
endcase
// Error! Compiler doesn't know anything about the test_t type
test_t test_struct;
// pack1::test_t test_struct;
initial begin
$display("P1 = %d", P1);
// Error at elaboration! Simulator cannot find the 'hello()' function
hello();
// pack1::hello();
end
endgenerate
endmodule
和F#Array
s(List
是*type*[]
的类型表示法)。您可以像这样修改它以使用列表:
Array
话虽如此,索引到let getrandomitem () =
let rnd = System.Random()
fun (combos : string list) -> List.nth combos (rnd.Next(combos.Length))
通常是一个坏主意,因为它具有O(n)性能,因为F#列表基本上是链表。如果可能的话,最好将List
放入数组:
combos
答案 2 :(得分:7)
我刚刚写了一篇关于这个主题的博文:http://latkin.org/blog/2013/11/16/selecting-a-random-element-from-a-linked-list-3-approaches-in-f/
在那里给出了3种方法,讨论了每种方法的性能和权衡。
总结:
// pro: simple, fast in practice
// con: 2-pass (once to get length, once to select nth element)
let method1 lst (rng : Random) =
List.nth lst (rng.Next(List.length lst))
// pro: ~1 pass, list length is not bound by int32
// con: more complex, slower in practice
let method2 lst (rng : Random) =
let rec step remaining picks top =
match (remaining, picks) with
| ([], []) -> failwith "Don't pass empty list"
// if only 1 element is picked, this is the result
| ([], [p]) -> p
// if multiple elements are picked, select randomly from them
| ([], ps) -> step ps [] -1
| (h :: t, ps) ->
match rng.Next() with
// if RNG makes new top number, picks list is reset
| n when n > top -> step t [h] n
// if RNG ties top number, add current element to picks list
| n when n = top -> step t (h::ps) top
// otherwise ignore and move to next element
| _ -> step t ps top
step lst [] -1
// pro: exactly 1 pass
// con: more complex, slowest in practice due to tuple allocations
let method3 lst (rng : Random) =
snd <| List.fold (fun (i, pick) elem ->
if rng.Next(i) = 0 then (i + 1, elem)
else (i + 1, pick)
) (0, List.head lst) lst
编辑我应该澄清,上面显示了一些从列表中获取随机元素的方法,假设您必须使用列表。如果它符合程序设计的其余部分,那么从数组中获取随机元素肯定更有效。