宇宙在膨胀,熵在增,人与人的时间线在疏远
不要让那些关心你的人担心啊…多打电话多联系
内心的空如点燃的纸洞
源于摩擦后的小火星、涨落中的小火苗
或许是又一种自我预言的实现
最终被填充物慢慢撑大
乐观不应是自我欺骗自我逃避
尽人事方能知天命
坠落、堕落
无论多深,终有浮力
好好学习、天天向上
做最好的自己
晚起的鸟儿の小窝
宇宙在膨胀,熵在增,人与人的时间线在疏远
不要让那些关心你的人担心啊…多打电话多联系
内心的空如点燃的纸洞
源于摩擦后的小火星、涨落中的小火苗
或许是又一种自我预言的实现
最终被填充物慢慢撑大
乐观不应是自我欺骗自我逃避
尽人事方能知天命
坠落、堕落
无论多深,终有浮力
好好学习、天天向上
做最好的自己
前些天学了点js,发现里边的箭头函数是真的好用,从此function关键字消失在了我的代码中。
但随之也发现了个问题,匿名函数遇上需要递归的地方该咋办呢……
在谷歌上不抱任何期待地敲下“匿名函数”“递归”等字样,没想到还真的有这样的操作。
Github上的大佬给出了答案。
https://github.com/Lucifier129/Lucifier129.github.io/issues/7 在 JavaScript
在 JavaScript 中用匿名函数(箭头函数)写出递归的方法
看起来还不错,越看越亢奋(装作看懂了的样子)
根据教程里的指导,求一个数阶乘可以写成这样的形式:
var fact_fac = self => num => num ? num * self(--num) : 1;
var Y = f => (x => n => f(x(x))(n))(x => n => f(x(x))(n));
var fact = Y(fact_fac);
console.log(fact(4));//24
解释一下:
先看第一行,
对于任意的函数self,这里边的fact_fac(源自factorial factory,阶乘工厂)满足
fact_fac(self) <=> num => num ? num * self(--num) : 1;
那么,当self为fact,其中fact也就是我们要的阶乘函数时,神奇的事情发生了!
fact_fac(fact) <=> num => num ? num * fact(--num) : 1;
而后者恰恰时fact函数的实现!!
这意味着有
fact_fac(fact) <=> fact
那么求解fact也就意味着要求解fact_fac的不动点。
而这,恰恰可以由另一个神奇的Y函数求得。具体实现可以自行维基Y组合子以及λ演算。
到这里,你会发现借助Y函数,所有的递归函数都可以用匿名函数表示,具体实现起来就是这样:
function a (...){
...a(...)...
return b;
}
可以等价于
Y = f => (x => n => f(x(x))(n))(x => n => f(x(x))(n));
a = Y(self => (...) => {
...self(...)...
return b;
});
那跟我们的主题“如何不用洋文编程”有什么关系呢?
别急,现在我们已经完成了第一步,彻底摆脱了function关键字。
稍微改造下上述代码,用$和_的组合代替变量名来装逼摆脱洋文。
(标点符号怎么能算洋文呢的!(~ ̄▽ ̄)~)
Y = $_=>(_=>$=>$_(_(_))($))(_=>$=>$_(_(_))($));
a = Y($=>(...)=>{...$(...)...return b;});
(当然,还有糟糕的return,这个呆会搞掉)
事实上,进一步了解js里的匿名函数后可以发现,它与lambda函数完全一致。
而lambda演算时图灵完备的,
这意味着我们的终极目标(不用洋文编程)是可行的!!
第二个需要解决的问题是循环的操作。
但是我们已经有了递归,解决一个while循环并不难。
直接上代码吧。
const Y = f => (x => n => f(x(x))(n))(x => n => f(x(x))(n));
const while_ = Y(self => judge => dosth => judge() && dosth() ^ self(judge)(dosth));
调用的基本语法是
while(a){b} <=> while_(()=>a)(()=>{b})
具体应用如下:
var test = 5;
while_(() => test > 0)(() => {
console.log('yes!!!');
test--;
});
简化一下:
const while_ = ($_=>(_=>$=>$_(_(_))($))(_=>$=>$_(_(_))($)))($=>_=>_$=>_()&&_$()^$(_)(_$));
当然也有相应的for循环版本:
const for_ = ($_=>(_=>$=>$_(_(_))($))(_=>$=>$_(_(_))($)))($=>_=>__=>$_=>_$=>{(_&&_())^__()&&_$()^$_()^$()(__)($_)(_$)});
调用的基本语法是:
for(a;b;c){d} <=> for_(()=>{a})(()=>b)(()=>{c})(()=>{d})
嫌for太丑太长,甚至自己造了个repeat函数,重复指定代码若干次。
const Y = f => (x => n => f(x(x))(n))(x => n => f(x(x))(n));
const repeat_ = Y(self => num => func => num && self(--num)() ^ func());
const repeat_ = ($_=>(_=>$=>$_(_(_))($))(_=>$=>$_(_(_))($)))($=>__=>_=>__&&$(--__)(_)^_());
相当于:
for(var i=0;i<a;i++){b/*without using i*/} <=> repeat_(a)(()=>{b})
OK,到此为止解决循环。
下一个问题是如何解决if等条件判断,这个好办,基本也有以下两种解决方案:
一种是三目运算符?:——不多说了。
另一种是利用&&和||以及&,|,^的性质:
if(a){b} <=> a&&b;
if(a){b}else{c} <=> a&&(b||1)||c;
1太丑,可以换成!0
两个语句间的分号也可以换成|、&、^等符号。
之前说的return也可以在这里解决。
注意到箭头函数箭头后可以直接返回值,那么可以采用以下的替换:
(a)=>{b; return c;} <=> (a)=>(b)&&0||c
善用++,可以让代码更简洁。
基本的操作就这些了,最后贴上开头处图片中的代码,欢迎大佬破译:
primes_below=$_$=>($=[])&($_=$)&(__=[])|(_=__)|_++^++_|($_=>(_=>$=>$_(_(_))($))(_=>$=>$_(_(_))($)))($=>_=>_$=>_()&&_$()^$(_)(_$))(()=>_<=$_$)(()=>{(_$=0)&(!__[_]&&($[$_++]=_))^($_=>(_=>$=>$_(_(_))($))(_=>$=>$_(_(_))($)))($=>_=>_$=>_()&&_$()^$(_)(_$))(()=>_$<=$_&&_*$[_$]<=$_$&&(_$==0||_%$[_$-!0]))(()=>{(__[_*$[_$]]=!0)|_$++})^_++})&&0||$;
console.log(primes_below(100));
PS. 切记,所有代码改写策略都要在非严格模式下执行,否则对变量的定义行为会产生洋文。
从2月18日购买screeps至今已经有近一周了,写些东西备忘。
当初听说有这样的编程游戏还激动了好一阵子,可惜我太菜,犹豫了好久才下的单。
为了玩screeps,寒假还特意学了点js。还好js的语法与c差不多。
(顺便一提,js的箭头函数和弱变量类型是真的舒服,但也经常会有些奇怪的操作,总之很迷)
人生第一次把api文档啃得那么仔细,为了建个能跑的脚本还反复过了好几遍的教程。
几天后的版本便能自动挖矿、升级、修路、刷墙了。
还在我的家(E17N28)击败了第一个侵略者,并收获了它的遗产。
甚至拓展了第二间room,开启了外矿之路。
感觉还是挺自豪的……
直到有人拆了我隔壁邻居的家,强占了他的room。
一看rcl等级,才3,不过如此——我都上5了。
碰巧敌人的防御creep是个憨憨。
这仇,必须得报。
进攻废了点功夫,不过,我还是占领了第三间room。
敌人被我赶跑,缩在自己老巢。
(有一说一,那个老巢地势真的好,易守难攻,我当初咋就没看见呢……E17N29)
夜深了,
我心满意足地睡觉去了……
第二天醒来,
发现家没了。
。。。
顿时人比前一天抽胡桃专武时歪了天空枪还难受……
。。。
好吧,一样难受。。。
我的家啊。。。
赶紧调录像,发现是我代码的一个逻辑bug。
当占领的room里的creep不断被干掉时,我的家会不断刷新creep。
导致自己把自己饿死了……
事前也想到过这种情况,但没想到的是,紧急搬运能量的creep也会由于能量不够,造不出来……
明明storage里还有300K的能量啊……
如果发现了这个问题,,
如果昨晚没为了进攻隔壁而把自家刷了好久的wall改建成rampart,,
如果睡前给新room开个安全模式,,
如果之前记得设置了自动安全模式打开机制,,
如果当时没贪、没懒,,
如果……
一个小bug或许短时间不会暴露出来,但一连串的小bug能拆了一个家。
这或许正是这个游戏的挑战之处。
毕竟coding一时爽,一直coding一直爽,debug火葬场。
总结教训:
在没有足够把握时,还是默默刷墙吧。
我的编程技巧暂时还支撑不起这个游戏。
这个寒假暂时退坑了。
厚积,薄发。
始以易简,伏变化之机,命之曰储能。后渐繁殊,极变化之致,命之曰效实。储能也,效实也,合而言之天演也。
——严复《天演论》
终日乾乾,夕惕若厉
第一次选考
国科大冬令营 物之道 北方的冬天
疫情 停课 腾讯课堂 知乎
王裔平 铭校
作文素材 英语好词 apple的笔记
数海漫游 数学网课 模拟卷
汽车 公路 逆风而行 高楼蝼蚁
英语练字 5楼教室 乐正绫 地铁工地 远方
天命
憨憨的 傻不拉叽的 脸 手 颈 毕业了
租借女友 裸考的物理 迟到的高考
三一 鄞中 上海 复旦
国科大 分别 家 宿舍
孤月 大学生
深秋 落叶 初雪 寒风 卷
深夜 教学楼 操场 球
教室 寝室 食堂
消愁
原神 安柏 刻晴 胡桃
跨年 人类一败涂地
Flag
1. 11点前不打游戏,11点后不吃东西
2. 自习去教学楼或图书馆
3. 1点半前必须上床,睡前刷牙,睡时锁手机
4. 最晚10点起床,必须吃早餐
5. 不要活在过去的影子之中,学会反省,学会思考
0. 终日乾乾,夕惕若厉。自律,慎独,不自欺。
Flag已经立下——你已经没有退路了
除法之所以叫“除”法,原来是因为表示了把东西去除掉的意思啊——人生第一次意识到……太激动了!!!
丰哥的解答:
高一时受学校图形计算器上原装贪吃蛇程序启发而写的C++小游戏
高三时整理压缩至100行
//By Eric Jin 2018 编译后切换至英文 a左d右w上s下 使用了Sleep() _kbhit() _getch() system("cls")等
#include <iostream>
#include <sstream>
#include <string>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <windows.h>
std::string o(int i)//4左6右8上2下
{
if (i == 0) return " ";
else if (i == 1) return "# ";
else if (i == 3) return "$ ";
else return "* ";
}
void drawmap(int h, int l, int maptest[100][100])//绘图
{
std::stringstream ss;
for (int i = 1; i <= h; i++)
{
for (int j = 1; j <= l; j++) ss << o(maptest[i][j]);
ss << std::endl;
}
std::string out = ss.str();
system("cls");
std::cout << out;
}
void findfood(int maptest[100][100], int& foodx, int& foody, int h, int l)//出食物
{
for (;;)
{
srand((int)time(0));
foodx = rand() % h + 1, foody = rand() % l + 1;
if (maptest[foodx][foody] == 0) break;
}
}
int gamein()
{
int speed, h = 20, l = 30, tail = 6, maptest[100][100], x = h / 2, y = l / 2, tx = h / 2, ty = l / 2 - tail, foodx, foody, dir = 6, tdir = 6, food = 0;
bool skip = 0;
std::cout << "Speed (100-slow 50-medium 10-fast):" << std::endl;
std::cin >> speed;
Sleep(500);
system("cls");
for (int i = 1; i <= h; i++)//设置边界
{
for (int j = 1; j <= l; j++)
{
maptest[i][j] = 0;
if (i == 1 || i == h) maptest[i][j] = 1;
}
maptest[i][1] = 1, maptest[i][l] = 1;
}
for (int i = ty + 1; i <= y; i++) maptest[x][i] = 6;
findfood(maptest, foodx, foody, h, l);
maptest[foodx][foody] = 3;
drawmap(h, l, maptest);
for (;;)
{
maptest[x][y] = dir;
x = (dir == 2) ? (x + 1) : (dir == 8 ? x - 1 : x), y = (dir == 6) ? (y + 1) : (dir == 4 ? y - 1 : y);
if (maptest[x][y] == 0 || maptest[x][y] == 3) maptest[x][y] = dir;
else
{
std::cout << std::endl << "You died." << std::endl << "Score: " << food << std::endl << "Speed: " << speed << std::endl;
std::cout << "0:exit" << std::endl;
std::cout << "1:new game" << std::endl;
char in1 = _getch();
std::cout << std::endl;
if (in1 == '0') return 0;
else return gamein();
}
if (skip == 0)
tx = (tdir == 2) ? (tx + 1) : (tdir == 8 ? tx - 1 : tx), ty = (tdir == 6) ? (ty + 1) : (tdir == 4 ? ty - 1 : ty), tdir = maptest[tx][ty];
else skip = 0;
maptest[tx][ty] = 0;
drawmap(h, l, maptest);
std::cout << "Score: " << food << std::endl << "Speed: " << speed << std::endl;
Sleep(speed);
if (_kbhit() != 0)
{
int in = _getch();
if (in == 97 && dir != 4 && dir != 6) dir = 4;
if (in == 115 && dir != 2 && dir != 8) dir = 2;
if (in == 119 && dir != 2 && dir != 8) dir = 8;
if (in == 100 && dir != 4 && dir != 6) dir = 6;
}
if (x == foodx && y == foody)
{
food++;
findfood(maptest, foodx, foody, h, l);
maptest[foodx][foody] = 3;
skip = 1;
}
}
}
int main()
{
return gamein();
}
高一时第一次接触了C++,心血来潮编了几个诸如推箱子、贪吃蛇、俄罗斯方块的小游戏。
觉得不过瘾,碰巧那会儿班里流行五子棋,便着手于编一个人机对战程序。
碍于编程知识不足,算法只用了判断、循环。借助easyx的图形库,它有了图形界面。
最终版本经调查测试,实力优于初学者,并能与中等水平者相抗衡。
(反正我被他阴了好多次,不知是该笑还是该哭(▼皿▼#) )
敌方每下一子,就在一个计数的矩阵里自己那格的四周(横竖撇捺四个方向,正负各延伸四格)加上个数值。这个加上的数值随与敌方所下子的距离而变。
不妨命名一个点的累积值为势,下子使周围势改变的效果为场。
己方下的子同理,但所给的场是反向的(即所增加的值为负值)。
用四个矩阵分别记录来自四个方向的场和相应积累的势,互不干扰。
最终己方落子的判断依据来源于所有方向的势的绝对值的总和,取绝对值的总和最大的点落子。
算法原理很简单,说白了就是赋值再取极值的贪心算法,但至于怎么赋值还是有点讲究的。
初始版本里采用了1-1-1-1赋值法(就是那个加上的值不随与上一落子的距离而变),并且没有加入四方向绝对值相加的机制,导致算法的最终行为很玄学。
它居然自动解锁了八卦阵技能,在连活三都不会防的情况下。(⊙_⊙)
升级版本采用了4-3-2-1赋值法,并延续到了最终版本。至此,它学会了如何防双三、三四等局面。
最终版本里又加入了遇四就堵的功能,但已经丧失了初始版本“两仪生四象,四象生八卦”的睿智。
废话讲完了,下面是代码
#pragma comment(lib, "kernel32")
#pragma comment(lib, "user32")
#pragma comment(lib, "gdi32")
#pragma comment(lib, "winspool")
#pragma comment(lib, "comdlg32")
#pragma comment(lib, "advapi32")
#pragma comment(lib, "shell32")
#pragma comment(lib, "ole32")
#pragma comment(lib, "oleaut32")
#pragma comment(lib, "uuid")
#pragma comment(lib, "odbc32")
#pragma comment(lib, "odbccp32")
#include <graphics.h>
#include <conio.h>
#include <math.h>
#include <stdlib.h>
int plotform(int x)
{
return 25 * (x - 1) + 15;
}
int storgeform(int x)
{
return (x - 15) / 25 + 1;
}
class whom
{
public:
int value;//1 or -1
COLORREF color;//BLACK or WHITE
};
void changeplayer(whom& whom)
{
whom.value = whom.value*(-1);
if (whom.value == 1)
whom.color = BLACK;
else
whom.color = WHITE;
}
void drawmap(int map[][20])
{
int xwidth = 480, ywidth = 480;
initgraph(xwidth, ywidth);
setbkcolor(YELLOW);
cleardevice();
setlinecolor(LIGHTGRAY);
for (int i = 15; i <= ywidth - 15; i = i + 25)
line(15, i, xwidth - 15, i);
for (int j = 15; j <= xwidth - 15; j = j + 25)
line(j, 15, j, xwidth - 15);
setfillcolor(LIGHTGRAY);
for (int i = 4; i <= 16; i = i + 6)//both i & j are storgeform
{
for (int j = 4; j <= 16; j = j + 6)
{
solidcircle(plotform(i), plotform(j), 5);
}
}
}
void human(int& x, int& y, int&xq, int& yq, whom& whom, int map[][20])//both x & y are storgeform
{
MOUSEMSG a;
do
{
do
{
a = GetMouseMsg();
} while (a.mkLButton == 1);
do
{
a = GetMouseMsg();
} while (a.mkLButton == 0);
xq = x;
yq = y;
x = round(((double)a.x - 15) / 25) + 1;
y = round(((double)a.y - 15) / 25) + 1;
/*if (getpixel(plotform(x),plotform(y)) == LIGHTGRAY)
break;*/
if (map[x][y] == 0)
break;
} while (1);
}
void record(int x, int y, whom whom, int map[][20], int maprecord[][20][20])//for robot
{
for (int i = 1; i <= 4; i++)
{
int dx, dy;
switch (i)
{
case 1:dx = 1, dy = 0; break;
case 2:dx = 0, dy = 1; break;
case 3:dx = 1, dy = -1; break;
case 4:dx = 1, dy = 1; break;
}
for (int j = -4; j <= 4; j++)
{
if (1 <= x + dx * j&&x + dx * j <= 19 && 1 <= y + dy * j&&y + dy * j <= 19 && map[x + dx * j][y + dy * j] == 0)
{
maprecord[i][x + dx * j][y + dy * j] = maprecord[i][x + dx * j][y + dy * j] + whom.value*abs(5 - abs(j));
}
}
}
}
void chong4(int x, int y,whom whom, int map[][20],int coordinate[3])//map[x][y]==whom.value
{
for (int i = 1; i <= 4; i++)
{
int dx, dy;
switch (i)
{
case 1:dx = 1, dy = 0; break;
case 2:dx = 0, dy = 1; break;
case 3:dx = 1, dy = -1; break;
case 4:dx = 1, dy = 1; break;
}
int m = 0, n = 0, skip = 0;
coordinate[0] = 0;
coordinate[1] = -1;
coordinate[2] = -1;
for (int j = 1;; j++)
{
if (x + j * dx < 1 || x + j * dx>19 || y + j * dy < 1 || y + j * dy>19)
break;
if (map[x + j * dx][y + j * dy] == whom.value)
{
m = m + 1;
if (skip > 0)
skip++;
}
else if (skip == 0 && map[x + j * dx][y + j * dy] == 0)
{
skip = 1;
coordinate[1] = x + j * dx;
coordinate[2] = y + j * dy;
}
else if (skip > 1)
{
coordinate[0] = 1;
break;
}
else
break;
}
if (coordinate[0] == 1)
skip = 1;
else
skip = 0;
for (int j = -1;; j--)
{
if (x + j * dx < 1 || x + j * dx>19 || y + j * dy < 1 || y + j * dy>19)
break;
if (map[x + j * dx][y + j * dy] == whom.value)
{
n = n + 1;
if (skip > 0)
skip++;
}
else if (skip == 0 && map[x + j * dx][y + j * dy] == 0)
{
skip = 1;
coordinate[1] = x + j * dx;
coordinate[2] = y + j * dy;
}
else if (skip > 1)
{
coordinate[0] = 1;
break;
}
else
break;
}
if (m + n >= 3&&coordinate[1]!=-1)
{
coordinate[0] = 2;
settextcolor(whom.value == 1 ? RED : BLUE);//used for debug
outtextxy(plotform(coordinate[1]), plotform(coordinate[2]), 'x');
return;
}
}
}
void robot(int& x, int& y, int& xq, int& yq, whom& whom, int map[][20], int maprecord[][20][20])//both x & y are storgeform
{
int max = -1;
changeplayer(whom);
record(x, y, whom, map, maprecord);
changeplayer(whom);
int coordinate[3];
chong4(xq, yq, whom, map, coordinate);
if (coordinate[0] == 2)
{
xq = x;
yq = y;
x = coordinate[1];
y = coordinate[2];
record(x, y, whom, map, maprecord);
return;
}
changeplayer(whom);
chong4(x, y, whom, map, coordinate);
if (coordinate[0] == 2)
{
xq = x;
yq = y;
x = coordinate[1];
y = coordinate[2];
changeplayer(whom);
record(x, y, whom, map, maprecord);
return;
}
changeplayer(whom);
for (int i = 1; i <= 19; i++)
{
for (int j = 1; j <= 19; j++)
{
if (map[i][j] == 0)
{
int potential[5];
for (int p = 1; p <= 4; p++)
{
potential[p] = maprecord[p][i][j];
if (abs(potential[p]) >= 5)
potential[p] = potential[p] * 2;
if (abs(potential[p]) >= 18)
potential[p] = potential[p] * 2;
}
int all = abs(potential[1]) + abs(potential[2]) + abs(potential[3]) + abs(potential[4]);
if (all > max)
{
max = all;
xq = x;
yq = y;
x = i;
y = j;
}
if (all == max && rand()%100 <= 60)
{
max = all;
xq = x;
yq = y;
x = i;
y = j;
}
}
}
}
record(x, y, whom, map, maprecord);
}
void drawpiece(int x, int y, whom& whom, int map[][20])//both x & y are storgeform
{
setfillcolor(whom.color);
solidcircle(plotform(x), plotform(y), 10);
map[x][y] = whom.value;
changeplayer(whom);
}
int main()
{
int map[20][20] = { 0 };
int maprecord[5][20][20] = { 0 };
whom whom;
whom.color = BLACK;
whom.value = 1;
int x, y, xq, yq;
drawmap(map);
do
{
if (whom.value == 1)
human(x, y,xq,yq, whom, map);
else
robot(x, y, xq, yq, whom, map, maprecord);
drawpiece(x, y, whom, map);
} while (1);
_getch();
closegraph();
}