CarYon 工作原理 - luosiwei-cmd/CarYon GitHub Wiki
需要了解 CarYon 的工作原理,才可以更好地使用 CarYon。
注意,在阅读本文之前,您需要阅读所有 Wiki 并且已经使用 CarYon 造过几组数据,这样您才能了解 CarYon 真正的实现细节。
认真读完本文后,您可以选择在光荣榜留名。
我们从一个最简单的 test.cpp
开始讲起。
下面是一个生成 A+B Problem 的 test.cpp
:
//a+b problem test
#include"caryon.h"
using namespace std;
using namespace ca;
int main(){
dataname="A+B_Problem_Test";
makein(1,10)/*make 10 in files*/{
csh();
inint(cyrand(0,100));
instring(" ");
inint(cyrand(0,100));
}
makeout(1,10);
closefile();
return 0;
}
头文件和命名空间不用讲。先来讲讲 dataname
,即前缀的实现。
dataname
在 caryon.h
的定义是 std::string dataname
,剩下的就在 test.cpp
中供使用者填写。
首先,我们得先了解 CarYon 最基本的工作原理。
CarYon 最基本的工作原理就是文件操作。其过程是建立文件,并将用户设定写入文件的东西写入文件。
那么输出只需要运行 test.exe
将生成的输入文件输入,并获得输出,将结果写入对应的文件就可以了。
所有生成的测试样例会被放在 data-dataname
中,所有 in
文件等等格式是 datanameno.in
,即dataname
+序号.in
。out
文件同理。
而 dataname
就是在 CarYon 建立文件夹和文件的时候,在写入那个freopen
命令的名称的前缀。
具体请看整个 namespace inout
的源码。请见下文。
然后我们来看 makein(start,end){}
,这个具体的实现非常简单,就是一个循环,为了用户。下面展示这个东西的宏定义:
#define makein(m, n) for (ci = m; ci <= n; ci++)
ci
是序号。因此,在使用 CarYon 的时候,请避免在 makein
中定义变量 ci
。
循环就是依次执行制作文件。
下面来看 csh();
这个函数必须包含在这里,为什么呢?我们先来看定义:
void csh() {
cnt = 0;
}
就这?cnt=0
?这里涉及着一些不可告人的秘密qwq。
下面makeout
的原理上文已经讲过。
closefile()
就是很简单地重定向到控制台。
上面我们就分析完了test.cpp
必备的几项技能。CarYon 的数据生成也就是这个原理。
程序的对拍全部集中在namespace cydebug
中,大体可以分为三个部分:
-
制作对拍文件
-
比较用户输出和标准输出
-
返回结果,输出日志
制作对拍文件也就是 myprogrm.exe
对应制作的输入文件其输出的文件,保存为 debug-dataname
文件夹,每个文件类型为 dataname
+序号+.ans
。但是这里和传统的数据生成还有一点不同。这里还记录了程序对应的运行时间,储存在 std::stringstream sp
中;
然后是比较,这里写了三个比较器,三个比较器的不同请参阅其他 Wiki。下面给出三个比较器的源码供参考:
FULLTest:
#include<bits/stdc++.h>
#include<ctime>
using namespace std;
#define UKE -1
#define AC 1
#define WA 0
int compareFile(FILE* file_compared, FILE* file_checked){
bool diff = 0;
int N = 65536;
char* b1 = (char*) calloc (1, N+1);
char* b2 = (char*) calloc (1, N+1);
size_t s1, s2;
do {
s1= fread(b1,1, N, file_compared);
s2 = fread(b2,1, N, file_checked);
if ((s1!=s2)||memcmp(b1, b2, s1)) {
diff = 1;
break;
}
}while(!feof(file_compared)||!feof(file_checked));
free(b1);
free(b2);
if (diff) return 0;
else return 1;
}
int main(int argc,char* argv[]){
if(argc<=2){
return UKE;
}
FILE* fuser=fopen(argv[1],"r");
FILE* fstd=fopen(argv[2],"r");
if(compareFile(fuser,fstd)==1){
return AC;
}
else {
return WA;
}
return 0;
}
NOIPStyle:
#include <bits/stdc++.h>
using namespace std;
#define UKE -1
#define AC 1
#define WA 0
int compareFile(FILE* file_compared, FILE* file_checked) {
bool diff = 0;
int N = 65536;
char b1[65536], b2[65536];
size_t s1, s2;
while (1) {
if (fgets(b1, N, file_compared) == NULL ||
fgets(b2, N, file_checked) == NULL) {
break;
}
s1 = strlen(b1);
s2 = strlen(b2);
if ((s1 != s2 && s1 + 2 != s2 && s1 - 2 != s2 && s1 + 1 != s2 &&
s1 - 1 != s2) ||
memcmp(b1, b2, min(s1, s2) - 1)) {
diff = 1;
break;
}
}
free(b1);
free(b2);
if (diff)
return 0;
else
return 1;
}
int main(int argc, char** argv) {
if (argc < 2) {
return UKE;
}
FILE* fuser = fopen(argv[1], "r");
FILE* fstd = fopen(argv[2], "r");
if (compareFile(fuser, fstd) == 1) {
return AC;
}
else {
return WA;
}
return 0;
}
IOIStyle:
#include<bits/stdc++.h>
using namespace std;
#define UKE -1
#define AC 1
#define WA 0
int cnt=1;
int compareFile(FILE* file_compared, FILE* file_checked){
bool diff=0;
int N=65536;
char b1[N],b2[N];
size_t s1,s2;
while(1){
cnt++;
if(fgets(b1,N,file_compared)==NULL||fgets(b2,N,file_checked)==NULL){
break;
}
s1=strlen(b1);
s2=strlen(b2);
if((s1!=s2&&s1+2!=s2&&s1-2!=s2&&s1+1!=s2&&s1-1!=s2)||memcmp(b1,b2,min(s1,s2)-1)){
cerr<<"Wrong Answer On Row "<<cnt-1<<", expected "<<b1<<", found "<<b2;
diff=1;
break;
}
}
free(b1);
free(b2);
if(diff)
return 0;
else
return 1;
}
int main(int argc,char* argv[]){
if(argc<=2){
return UKE;
}
FILE* fuser=fopen(argv[1],"r");
FILE* fstd=fopen(argv[2],"r");
system("mkdir IOI");
freopen("IOI./IOI.log","w",stderr);
if(compareFile(fuser,fstd)==1){
cerr<<"Accept.\n";
return AC;
}
else {
return WA;
}
return 0;
}
比较文件就是要针对每一个文件,先判断其是否超出运行时间,直接输出 TLE,反之,利用比较器的返回值判断 WA 或 AC。
于是就是下面这段经典代码:
std::string command = "tools\\Compare\\NOIPStyle.exe debug-" +
dataname + "\\" + dataname + Debug1 +
".ans data-" + dataname + "\\" + dataname +
Debug1 + ".out";
int flag = system(command.c_str());
sp >> runtime;
freopen("Debug.log", "a", stdout);
if (runtime > maxtime) {
std::cout << "TestCase " << i
<< ", result: TLE. The program\'s runtime is " << runtime
<< " ms.\n";
}
else if (flag == 1) {
std::cout << "TestCase " << i << ", result: AC.\n";
}
else if (flag == 0) {
std::cout << "TestCase " << i << ", result: WA.\n";
}
else {
std::cout << "TestCase " << i << ", result: UKE.\n";
}
这是整个比较的核心。
然后是写入日志,也在上面的源码里面了。
这一段的实现主要是编写一个解释器,用来解释对应的配置文件从而生成 test.cpp
。其可以分为三个领域。
对应每一行生成一行 test.cpp
。具体的实现请看之后给出的源码。
当检测到 CHECKER 返回的信息异常或者处理异常的时候,会报错并渲染失败。
检查 test.cpp
的格式和变量名,分支,循环是否正常。
下面给出渲染器的源码:
首先是头文件:
#ifndef ANALYSIS_HPP
#define ANALYSIS_HPP
#include<bits/stdc++.h>
#include<io.h>
#include<windows.h>
using namespace std;
struct _finddata_t info;
ofstream fout("test.cpp");
std::string type;
int start_d,end_d;
int CNT,_CNT,if_cnt,for_cnt,tab_cnt;
string _variable[10000]={""};
int _i=0;
string maketab(){
clog<<"[CHECKER] Checking how many space(s) will be used.\n";
string temp;
temp=" ";
tab_cnt=if_cnt+for_cnt;
int i;
for(i=0;i<tab_cnt;i++){
temp+=" ";
}
clog<<"[CHECKER] For row "<<CNT<<", there will be "<<i*4+8<<" space(s) here.\n";
return temp;
}
void init(){
fout<<"#include\"caryon.h\"\n";
fout<<"using namespace std;\n";
fout<<"using namespace ca;\n";
fout<<"\n";
fout<<"int main(){\n";
}
void end(){
fout<<" }\n";
fout<<" makeout("<<start_d<<","<<end_d<<");\n";
fout<<" return 0;\n";
fout<<"}\n";
cout<<"Analysis end with succeed.\n\n";
cout<<"Please check the analysis.log for more information.\n";
system("pause");
}
void addMakein(int a,int b){
fout<<" makein("<<a<<","<<b<<"){\n";
fout<<" csh();\n";
clog<<"[LOG] Makein function was written successfully.\n";
}
void addInint(int a,int b){
fout<<maketab()<<"inint(cyrand("<<a<<","<<b<<"));\n";
clog<<"[LOG] Make a random number successfully.\n";
}
void addInint(string a){
fout<<maketab()<<"inint("<<a<<");\n";
clog<<"[LOG] Make a number successfully.\n";
}
void addInstring(string a){
fout<<maketab()<<"instring(\""<<a<<"\");\n";
clog<<"[LOG] Make a string or a space successfully.\n";
}
void addInstring(int a){
fout<<maketab()<<"instring(cyrand_word("<<a<<"));\n";
clog<<"[LOG] Make a random string successfully.\n";
}
void makedataERR(){
cerr<<"[ERR] on row "<<CNT<<", more than 1 makedata operation is not be allowed. | The makedata operation may be not in the fitst row. Plese check.\n";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void NothatERR(){
cerr<<"[ERR] on row "<<CNT<<", unknown operation found.\n";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void NoMakedataERR(){
cerr<<"[ERR] there is not a makedata opertion in your compile file.\n";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void NoiniERR(){
cerr<<"[ERR] cannot find file: control.ini.\n";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void variWrongERR(){
cerr<<"[ERR] on row "<<CNT<<", the variable name is wrong.\n";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void twoVariERR(){
cerr<<"ERR: on row "<<CNT<<", the variable name has already been named.\n";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void nothisVariERR(){
cerr<<"[ERR] on row "<<CNT<<", there is no variable name "<<type<<".\n";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void NoForEndERR(){
cerr<<"[ERR] The for do not have an end.";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void addInt(string type){
fout<<maketab()<<"int "<<type<<";\n";
clog<<"[LOG] Add int variable "<<type<<" successfully.\n";
_variable[_i++]=type;
}
void addIntAssign(string type,int a){
fout<<maketab()<<type<<"="<<a<<";\n";
clog<<"[LOG] Assign variable "<<type<<" to "<<a<<" successfully.\n";
}
bool isvari(string type){
clog<<"[CHECKER] Checking if "<<type<<" is a variable name.\n";
if(!((type[0]>='a' and type[0]<='z')or(type[0]>='A' and type[0]<='Z')or(type[0]=='_')or(type[0]=='$'))){
return false;
}
for(int i=1;i<type.size();i++){
if(!((type[i]>='a' and type[i]<='z')or(type[i]>='A' and type[i]<='Z')or(type[i]=='_')or(type[i]=='$')or(type[i]>='0' and type[i]<='9'))){
clog<<"[CHECKER] "<<type<<" is not a variable name.\n";
return false;
}
}
clog<<"[CHECKER] "<<type<<" is a variable name. Accept.\n";
return true;
}
bool alreadyhave(string type){
clog<<"[CHECKER] Checking if variable "<<type<<" has already used.\n";
for(int i=0;i<_i;i++){
if(_variable[i]==type){
clog<<"[CHECKER] Variable "<<type<<" has already used.\n";
return false;
}
}
clog<<"[CHECKER] Variable "<<type<<" has not been used yet.\n";
return true;
}
void addvariInint(string type){
fout<<maketab()<<"inint("<<type<<");\n";
clog<<"[LOG] Make a variable number successfully.\n";
}
void assignrandvari(int start,int end,string type){
fout<<maketab()<<type<<"=cyrand("<<start<<","<<end<<");\n";
clog<<"[LOG] Assign variable "<<type<<" with a random number successfully.\n";
}
void addFor(string i,int start,int end,int add){
fout<<maketab()<<"for("<<i<<"="<<start<<";"<<i<<"<="<<end<<";"<<i<<"+="<<add<<"){\n";
clog<<"[LOG] Add a for successfully.\n";
for_cnt++;
}
void endFor(){
for_cnt--;
fout<<maketab()<<"}\n";
clog<<"[LOG] End a for successfully.\n";
}
void NotinForERR(){
cerr<<"[ERR] On row "<<CNT<<", the continue/break/sf is not in a for.\n";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void NotinIfERR(){
cerr<<"[ERR] On row "<<CNT<<", the fi is not in an if.\n";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void NoIfEndERR(){
cerr<<"[ERR] A/Some if(s) do not have an end.";
fout<<"\n\n//Analysis failed, plese check stderr to know more.\n";
cout<<"Analysis failed. Please check the analysis.log for more information.\n";
system("pause");
exit(0);
}
void addContinue(){
fout<<maketab()<<"continue;\n";
clog<<"[LOG] Add a \"continue\" successfully.\n";
}
void addBreak(){
fout<<maketab()<<"break;\n";
clog<<"[LOG] Add a break successfully.\n";
}
void addIf(string type){
fout<<maketab()<<"if("<<type<<"){\n";
clog<<"[LOG] Add an if successfully.\n";
if_cnt++;
}
void endIf(){
if_cnt--;
fout<<maketab()<<"}\n";
clog<<"[LOG] End an if successfully.\n";
}
void addForWithVariEnd(string a,int b,string c,int d){
fout<<maketab()<<"for("<<a<<"="<<b<<";"<<a<<"<="<<c<<";"<<a<<"+="<<d<<"){\n";
clog<<"[LOG] Add a for with a variable end successfully.\n";
for_cnt++;
}
bool endForc(){
clog<<"[CHECKER] Checking if sf/continue/break is in a fs.\n";
if(for_cnt){
clog<<"[CHECKER] Sf/continue/break is in a fs.\n";
return true;
}
else{
clog<<"[CHECKER] Sf/continue/break is not in a fs.\n";
return false;
}
}
bool endIfc(){
clog<<"[CHECKER] Checking if fi is in an if.\n";
if(if_cnt){
clog<<"[CHECKER] Fi is in an if.\n";
return true;
}
else{
clog<<"[CHECKER] Fi is not in an if.\n";
return false;
}
}
#endif //#ifndef ANALYSIS_HPP
/*ANALYSIS.HPP*/
然后是源文件:
#include "analysis.hpp"
using namespace std;
int main() {
freopen("analysis.log", "w", stderr);
init();
if (_findfirst("control.ini", &info) == -1) {
NoiniERR();
}
ifstream fin("control.ini");
while (fin >> type) {
CNT++;
if (type == "makedata") {
_CNT++;
if (CNT != 1) {
makedataERR();
}
fin >> start_d >> end_d;
addMakein(start_d, end_d);
}
else if (type == "inint") {
fin >> type;
if (type == "cyrand") {
int start, end;
fin >> start >> end;
addInint(start, end);
}
else if (type == "variable") {
fin >> type;
if (!alreadyhave(type)) {
addvariInint(type);
}
else {
nothisVariERR();
}
}
else {
addInint(type);
}
}
else if (type == "instring") {
fin >> type;
if (type != "cyrand")
addInstring(type);
else {
int a;
fin >> a;
addInstring(a);
}
}
else if (type == "space") {
addInstring(" ");
}
else if (type == "int") {
fin >> type;
if (isvari(type)) {
if (alreadyhave(type))
addInt(type);
else
twoVariERR();
}
else {
variWrongERR();
}
}
else if (type == "assignment") {
fin >> type;
int _a;
fin >> _a;
if (!alreadyhave(type)) {
addIntAssign(type, _a);
}
else {
nothisVariERR();
}
}
else if (type == "variable_cyrand") {
fin >> type;
int start, end;
fin >> start >> end;
if (alreadyhave(type)) {
nothisVariERR();
}
else {
assignrandvari(start, end, type);
}
}
else if (type == "fs") {
string __i;
fin >> __i;
int ___start, ___end, ___add;
fin >> ___start >> ___end >> ___add;
if (!alreadyhave(__i))
addFor(__i, ___start, ___end, ___add);
else {
type = __i;
nothisVariERR();
}
}
else if (type == "fs_end_withvari") {
string __i, __j;
fin >> __i;
int ___start, ___add;
fin >> ___start >> __j >> ___add;
if (alreadyhave(__i) || alreadyhave(__j)) {
if (alreadyhave(__i)) {
type = __i;
nothisVariERR();
}
else {
type = __j;
nothisVariERR();
}
}
else {
addForWithVariEnd(__i, ___start, __j, ___add);
}
}
else if (type == "sf") {
if (endForc())
endFor();
else
NotinForERR();
}
else if (type == "continue") {
if (endForc()) {
addContinue();
}
else {
NotinForERR();
}
}
else if (type == "break") {
if (endForc()) {
addBreak();
}
else {
NotinForERR();
}
}
else if (type == "if") {
fin >> type;
addIf(type);
}
else if (type == "fi") {
if (endIfc())
endIf();
else
NotinIfERR();
}
else {
NothatERR();
}
}
if (_CNT == 0) {
NoMakedataERR();
}
if (for_cnt) {
NoForEndERR();
}
if (if_cnt) {
NoIfEndERR();
}
end();
}
这些都是核心。
所有集中的功能都放在一个命名空间里。最终用户使用的 namespace ca
是包含了所有命名空间的集合。您也可以利用单独的命名空间使用具体的函数。这在老版的编译器中会降低执行时间。但是新版编译器并不会。
下面给出 namespace ca
的定义:
namespace ca {
using namespace crand;
using namespace cmath;
using namespace inout;
using namespace cydebug;
using namespace cconst;
using namespace cgraph;
#if __cplusplus >= 201103L
using namespace caryon_cpp11;
#endif //#if __cplusplus>=201103
using namespace cgeo;
} // namespace ca
这里有很多命名空间,我们一个一个讲。
这个命名空间包含着 CarYon
的随机数库。
几乎所有和随机有关的 CarYon 内置函数都在此定义。
下面给出源码:
namespace crand {
bool isInit;
int MTindex;
ll MT[624];
void csrand(int seed) {
MTindex = 0;
isInit = 1;
MT[0] = seed;
for (int i = 1; i < 624; i++) {
int t = 1812433253 * (MT[i - 1] ^ (MT[i - 1] >> 30)) + i;
MT[i] = t & 0xffffffff;
}
}
inline void generate() {
for (int i = 0; i < 624; i++) {
long long y = (MT[i] & 0x80000000) + (MT[(i + 1) % 624] & 0x7fffffff);
MT[i] = MT[(i + 397) % 624] ^ (y >> 1);
if (y % 2 == 1)
MT[i] ^= 2147483647;
}
}
inline int cyrand() {
if (!isInit)
csrand((int)time(NULL));
if (MTindex == 0)
generate();
int y = MT[MTindex];
y = y ^ (y >> 11);
y = y ^ ((y << 7) & 1636928640);
y = y ^ ((y << 15) & 1022730752);
y = y ^ (y >> 18);
MTindex = (MTindex + 1) % 624;
return y;
}
inline ll cyrand_ll() {
return ((ll)(cyrand()) << 31) + cyrand();
}
inline int cyrand(int a, int b) {
if (a > b)
a = b;
if (a == b)
return a;
else
return cyrand() % (b - a + 1) + a;
}
inline ll cyrand_ll(ll a, ll b) {
if (a > b)
a = b;
if (a == b)
return a;
else
return cyrand_ll() % (b - a + 1) + a;
}
inline bool cyrand_bool() {
if (cyrand(0, 1) == 0)
return false;
else
return true;
}
inline char cyrand_letter() {
return cyrand(32, 126);
}
inline char cyrand_engs() {
return 'a' + cyrand(0, 26);
}
inline char cyrand_engb() {
return 'A' + cyrand(0, 26);
}
inline char cyrand_fomatc() {
switch (cyrand(0, 4)) {
case 0: {
return '\n';
break;
}
case 1: {
return '\t';
break;
}
case 2: {
return '\r';
break;
}
case 3: {
return '\v';
break;
}
case 4: {
return '\f';
break;
}
}
}
inline std::string cyrand_word(int a) {
std::string chen_zhe;
for (int kkksc03 = 0; kkksc03 < a; kkksc03++) {
if (cyrand_bool()) {
chen_zhe[kkksc03] = cyrand_engs();
}
else {
chen_zhe[kkksc03] = cyrand_engb();
}
}
return chen_zhe;
}
inline std::string cyrand_article(int a) {
std::string soha;
soha = cyrand_word(cyrand(1, 10));
for (int lzn = 1; lzn < a; lzn++) {
soha = soha + " " + cyrand_word(cyrand(1, 10));
}
return soha;
}
template < typename T >
inline T choice(T* a, int lbound, int ubound) {
return a[cyrand(lbound, ubound)];
}
inline double doubleRandom() {
srand(time(0));
return (double)(rand() / (double)RAND_MAX);
}
inline bool __checktmp(int* arr, int tmp, int flag, int end) {
if (flag == 0) {
return true;
}
for (int i = 0; i < end; i++) {
if (flag == -1) {
if (arr[i] == tmp) {
return false;
}
}
else if (flag == 1) {
if (arr[i] > tmp) {
return false;
}
}
else if (flag == 2) {
if (arr[i] < tmp) {
return false;
}
}
else if (flag == 3) {
if (arr[i] >= tmp) {
return false;
}
}
else if (flag == 4) {
if (arr[i] <= tmp) {
return false;
}
}
}
return true;
}
inline void randomArr(int* arr, int n, int lower, int upper, int flag) {
for (int i = 0; i < n;) {
int tmp = cyrand(lower, upper);
if (__checktmp(arr, tmp, flag, i - 1)) {
arr[i++] = tmp;
}
}
}
} // namespace crand
函数的功能都可以通过一些 Wiki 查看。
其中有几个特殊的函数是内部函数,并没有在 Wiki 中提出。如果您有更好的实现这些函数的方法,欢迎进行 Pull Request。
其中还有一个工具函数:
template < typename T >
inline T choice(T* a, int lbound, int ubound) {
return a[cyrand(lbound, ubound)];
}
请查看 工具函数 Wiki 了解该功能的使用。
这些随机的东西是您生成数据的重要组成部分。
数学库和几何库,不解释。
负责最重要的组成部分。分别是生成文件和程序对拍。instring,inint,in,makeout,debug
等函数都内含于此。
负责常用常量。
随机树和随机图。
C++11 的 CarYon 特别支持。源码如下:
#if __cplusplus >= 201103L
namespace caryon_cpp11 {
template < typename T >
T choice_plus(std::initializer_list< T > a) {
std::vector< T > b;
for (auto i : a) {
b.push_back(i);
}
return b[crand::cyrand(1, b.size()) - 1];
}
} // namespace caryon_cpp11
#endif //#if __cplusplus>=201103
内含一个工具函数choice_plus({0,1,...,2,4})
。
了解了 CarYon 的工作原理后,您可以自由地使用 CarYon 了。不再受任何文档的拘束!您甚至可以在光荣榜留名。