Stelpolva Write

Reader

Read the latest posts from Stelpolva Write.

from selent

做镜牢任务顺手认认,信息来自游戏本体和wiki

斩击

等级 名称 效果
手术用刀 [4 5++]拥有斩击技→下回合+(斩击技数 +1)层迅捷
虚饰的和平 [5 6++]速度差≥3(2+)→斩伤+15%(20%+)
磨损的砥石 [3]斩击技攻击等级+(斩击技数)(max+3)
攻击斩抗>1.0单位时→伤+(5+目标的斩击抗性×10)%(max+25%)
梦中的电子羊 紫/斩击击杀→2t斩威力+2
紫完全共鸣→战斗开始全体攻击防御等级+2
裁缝用剪刀 [4 6++]硬币数≤2的斩击技→拼点威力+1,伤+10%(15%+ 20%++)
单币拼点威力+1,伤+20%(25%+ 30%++)
拐杖短剑 斩击(7协全部攻击 弱点分析+ 拼点威力-1++)拼点胜利→下回合目标防御等级-2(每t每人1次不叠加)
关卡开始(奇数回合+ 每回合++)7协+拼点威力(除自身外7协的友方单位数/2)(max2),(拼点胜利伤+20%++)
决意 斩击技≥2→t1拼点威力+1,t2起+1(2+)速、拼点(最终)威力+1
4协+hp至上限的75%(50%+ 49%++)但受到的伤害与恢复-50%(75%+)
宣判之刻 [3 4 5+ 6++]单方面攻击斩伤+10%(12.5%++)
拼点胜利斩伤+(40(50++)/剩余硬币数)%
云纹鹤颈瓶 斩击1技能(黑云会所有斩击 橙+ 所有攻击++)拼点威力+1,命中时目标攻击等级-2
回合开始时,黑云会威力+1,攻击命中并+流血强度/层数时自身+(1/2++)呼吸法强度(每回合每人最多4次 6+ 8++)
切开的记忆 [1 2]斩击技基础威力+2,硬币威力+1(包括E.G.O)
斩击拼点胜利伤+30%,该技能命中时,每hp-5%伤+5%(max+20%)
 
阅读更多

from senioria

The problem: expression parsing with operator precedence

Human-readable languages tends to have a lot of infix operators with various precedences. In CFG, this corresponds to an annoying list of different rules, e.g.:

add : add '+' mul | add '-' mul | mul
mul : mul '*' atom | mul '/' atom | mul '%' atom | atom
// etc.

With good error recovery necessary, normal parsing methods would need to repeat the rules. Assuming it's possible to collapse the operators, it's still necessary to name each rules, and repeat the names three more times: for recursion and in the operation of one level lower priority as operand and fallback.

To make things worse, since operations like subtraction are not associative, left-associative operators (i.e. the leftmost subexpression is evaluated first) is inevitable, which also requires special handling in parser generation methods like PEG or LL, and naively implemented parser combinators.

So there's need for a better parsing strategy, which allows us to write less rules, and handle the details in parsing automatically. One solution is Pratt parsing. It not only provides a strategy to easily designate the precedence and associativity of infix operators, but is also capable of processing both prefix and postfix operators, with a little extension.

The idea and an implementation for infix operators

The key idea of Pratt parsing is the binding power of the operators. In an expression of atoms and infix operators, the atoms can be seen to be separated by the operators, e.g. in 1 + 2 * 3 - 4, each operators have exactly two atoms at both end of it, and the atom must be at the end of the subexpression, which is an operand of the operator.

In other words, the operands of all operators are “bind” to their ends. Thus we can imagine that in case when two operators are “fighting for” the same operand, the operator binding tighter wins, and associate with the operand first, then the second operator. E.g. in 1 + 2 * 3 - 4, * wins in both ends, and gets 2 * 3 forms a subexpression first, then + and -, and in 1 + 2 * 3 ^ 4, * loses to ^, but wins +, so 3 ^ 4 gets bound first, then 2 * (3 ^ 4), + comes the last.

That's the idea: designate each operator with a binding power, and when two operators fight, make the one with the higher binding power win.

Surprisingly, this mechanic can also be used to deal with associativity. As a clarification, associativity only applies when two operators of the same precedence chains, e.g. in 1 * 2 + 3 * 4, the * wouldn't be burdened with associativity, since they binds with their atoms eagerly, and don't fight with each other, but in 1 + 2 * 3 - 4, + and - would fight with each other, thus their associativity applies.

The solution is the same: since associativity involves determining the operator at which side wins, we can designate the operators' ends with different binding power, thus operators with the right end a stronger binding power would have the operator at the left win, and therefore is left associative, and vice versa.

Now it's time for the implementation. There's a straightforward introduction and recursive implementation in this blog (which may be better than this one x x). Here we give an implementation by stack and a loop, with the chumsky parser combinator library, and we'll explain the algorithm later:

// `Oper` is the type of operators, `Expr` is the type of expressions.
// `atom` is the parser to parse an atom, and `oper` is the parser to parse an operator.
enum State {
    Expr,
    Oper,
}
custom(move |inp| {
    let mut ops: Vec<Oper> = vec![];
    let mut exprs = vec![];
    let mut state = State::Expr;
    loop {
        // Read the next token according to the state
        let Some(op) = (match state {
            State::Expr => {
                state = State::Oper;
                exprs.push(inp.parse(&atom)?);
                continue;
            }
            State::Oper => {
                state = State::Expr;
                inp.parse(oper.or_not())?
            }
        }) else {
            break;
        };
        // Bind the previous operators
        let cp = op.bindpow().0;
        while let Some(pre) = ops.last().copied() {
            let pp = pre.bindpow().1;
            if pp < cp {
                break;
            }
            ops.pop();
            // there's no better way of popping multiple elements from a Vec in Rust ><
            let Some((r, l)) = (try { (exprs.pop()?, exprs.pop()?) }) else {
                unreachable!()
            };
            exprs.push(Expr::Op(pre, Box::new((l, r))));
        }
        ops.push(op);
    }
    // Finally: collect the rest items in the stack into an expression.
    // Since they didn't get bound before, their precedence must be in ascending order,
    // binding reversly is correct.
    let Some(mut res) = exprs.pop() else {
        unreachable!()
    };
    while let Some(op) = ops.pop() {
        let Some(v) = exprs.pop() else { unreachable!() };
        res = Expr::Op(op, Box::new((v, res)));
    }
    Ok(res)
})

Here we have maintain a state machine, which has the following states: the initial state, the previous item being an operator, the previous item being an expression, and the final state. Since we are parsing a sequence with atoms first, and operator and atom alternating, the item to be expected at each state is certain. Since the states are only used to fetch the items, we merged the states.

Now that we have the item sequence, we need to compose the sequence into the result expression.

Consider the information we want and we have: we want to know when to merge the expressions around an operator; and at each step, when we get an atom, we get nothing, since we already know it would be there; and for each operator, we can only know whether it can bind the expressions around it when the other two operators around it are known. Luckily, when we get an operator, we've already known all operators on its left, so we only need to get the operator on its right to know if the operator can be bound.

At this step we can have a figure about the algorithm: when we get an operator, check if the operator on its left can be bound, and regardless of whether the last operator binds, store the current operator and wait for the next operator. When we should store multiple operators, it's obvious that their binding powers should be ascending, since if not so, for the decreasing operator, all previous operators with stronger binding power should be bound before it.

Thus in the rest of our algorithm, we maintain a stack for the operators and expressions, ensure the ascendance of the operators before pushing, and finally bind all the rest operators in the stack: since they're ascending, binding them from later to earlier is correct.

That's all.

Patches for prefix and postfix operators

Despite being true in most languages, it's false that prefix and postfix operators must have higher precedence than infix operators. A seemingly ridiculous but applicable example is the if expr then expr else expr structure, where if can be a prefix operator, and then and else are postfix, such that the whole structure is actually parsed as (((if expr) then expr) else expr).

Our analysis above still applies here: a prefix operator must fight with the infix operator after it, before it can bind to its operand, due to the precedence rules, that's also true for postfix operators, but the directions are reversed. Thus for prefix operators, we only need to wait for the next operator and compare their binding powers, as what we've done for infix operators; the only difference is that instead of popping two expressions, we only need to pop one element to bind to the prefix operator. For postfix operators, things are better —– they can be bound instantly when read, without entering the stack.

Conveniently, we can note that in an AST with reasonably design, whether a operator is prefix, infix or postfix is inherent, thus we can just ask for themselves and store them in the same stack:

custom(move |inp| {
    let mut ops: Vec<Oper> = vec![];
    let mut exprs = vec![];
    let mut state = State::Expr;
    loop {
        let Some(op) = (match state {
            // Prefixes are processed like exprs, except that they don't change the state.
            // Also, they should be pushed to diff stacks.
            State::Expr => {
                match inp.parse(&atom)? {
                    Atom::Expr(e) => {
                        state = State::Expr;
                        exprs.push(e);
                    }
                    Atom::Op(o) => ops.push(o),
                }
                continue;
            }
            State::Oper => {
                state = State::Expr;
                inp.parse(oper.or_not())?
            }
        }) else {
            break;
        };
        let cp = op.bindpow().0;
        while let Some(pre) = ops.last().copied() {
            let pp = pre.bindpow().1;
            if pp < cp {
                break;
            }
            ops.pop();
            // Prefix opers are checked and bound like infix opers,
            // only note that their operand numbers diff
            if pre.is_bin() {
                let Some((r, l)) = (try { (exprs.pop()?, exprs.pop()?) }) else {
                    unreachable!()
                };
                exprs.push(Expr::BinOp(pre, Box::new((l, r))));
            } else {
                let Some(v) = exprs.pop() else {
                    unreachable!()
                };
                exprs.push(Expr::UnOp(pre, Box::new(v)));
            }
        }
        // With higher precedence opers bound, postfix opers can safely bind now
        if op.is_postfix() {
            let Some(v) = exprs.pop() else {
                unreachable!()
            };
            exprs.push(Expr::UnOp(op, Box::new(v)));
        } else {
            ops.push(op);
        }
    }
    let Some(mut res) = exprs.pop() else {
        unreachable!()
    };
    while let Some(op) = ops.pop() {
        // The same as above, process infix and prefix opers diffly
        if op.is_bin() {
            let Some(v) = exprs.pop() else {
                unreachable!()
            };
            res = Expr::BinOp(op, Box::new((v, res)));
        } else {
            res = Expr::UnOp(op, Box::new(res));
        }
    }
    Ok(res)
})
 
Read more...

from 漫游者迷路中

《包法利夫人》一书大概是我二月唯一读完的一本书,回想到自己还能快乐读“杂书”的时候一个月可以看完好多本书,已经许久没有这样一口气读一本文学作品了。以前是没有写读书笔记的习惯,更不会为读了一本书而专门写一篇小记,现在只不过是因为看到一些fedi友与博主的读书小记突然有了三分钟热情,虽只是拙劣模仿,但也是我的一种开始吧。第一次尝试可能写得奇形怪状的,希望以后的自己看到不要过于责怪自己,但想到自己会因为自己以前笔记写得丑而生气估摸是困难的。

无厘头的缘由

其实书单列了很多书,但突然要看这本是有点幽默的原因的。在B站UP主戒社的直播中,投稿人引用了包法利夫人,虽然那位投稿人尽显高傲,以至于令人恶心,不过还是让我突然想起这本书我早有兴趣却没有看。于是我将阅读此书提上了日程,并速速看完了。

挣扎的阅读过程

不知道是太久不看如此正经的文学作品,还是吸取了太多番茄无脑网络小说,也许还有电子屏幕的问题,无论如何这次读书启动十分艰难,刚刚读个开头就令我头疼。 『▷插播一条,谁有不用的kindle送我』 加上我本人对外国人名字非常不敏感,很容易看着看着就糊涂了,这更是令我读书的时候有点“断片”。不过我曾经可是画过《基督山伯爵》人物关系图的,我有这个毅力我什么都会成功的。 总之我还是逼着自己把本书看完了,并且看了第二遍,在知道几个人物关系之后更加顺滑了。

不深刻的理解

啰啰嗦嗦终于要写正题了,爱玛在我眼中是一个受害者,但并非是纯粹的受害者。爱玛是一个浪漫主义者,追求浪漫已经成为她的一种常态,她对爱情的认识是从书里“学”来的,如她所说“爱情仿佛一只玫瑰色羽毛的巨鸟”。面对结婚后平淡的生活,爱玛内心产生了不满与自我困惑,她充满了对现实生活的厌倦和对理想化生活的渴望。在当时的社会条件下,爱玛作为女性却也是被束缚着的,她没有独立改变现状的思想,她所谓的充满“欢愉、热情和迷恋”的爱情都需要一个翩翩公子才能实现。爱玛就这样被裹挟着走入歧途。 但爱玛自己也推动自己在泥沼里越陷越深,过度的虚荣、对激情刺激的追求、不理智的判断,加上她不断透支经济、欺骗丈夫,她不仅加害了自己,更是使家庭受到了伤害。 爱玛最后的毁灭是经济上的崩溃,债务危机的爆发,情人的逃避,她求助无门选择了自杀,而爱玛的一切与高额的债务也使她丈夫死亡。这一切都拼接成了一幕幕荒诞的悲剧,爱玛到最后也没有找到自己幻想中的爱情,她理解不了自己,别人也理解不了她,就如同最后也查不出的病因无头无尾。

我在想

那么到底要感悟什么呢?是要对爱情忠贞?是要停止不理智的幻想?是要远离借贷?还是要感受到社会吃人的事实? 事实上,因为我个人的爱情观和价值观等,与那个社会的太有距离感,所以看的时候我确实脑子里在打架,一边理解那个时候好像确实这样,一边又满脑子“为什么啊?!” 但看到最后我已经完全放弃逼迫自己一定要感悟些什么,好像爱玛已经化成一种固有的形象留在心里了,她就这样变成一种缩影把悲剧写在了她一个人身上。

死去

一些跳脱

①“福楼拜在解剖包法利夫人”大概是指福楼拜将爱玛·包法利的一生像解剖标本一样,细致地展示给读者。我就说我看完我能懂吧。读的时候感觉福楼拜的描写很细腻,但又冷静克制,有一种旁观者的感觉,真的吗,我不知道。

②虽然一直呈现了查理·包法利医生不懂浪漫、平庸……但是爱玛死的时候他提出:“我希望她入殓时,身穿她的新婚礼服,脚着白鞋,头戴花冠,头发披在两肩,一棺两椁:一个用橡木,一个用桃花心木,一个用铅。我不要人和我谈话;我会硬撑起来的。拿一大幅绿丝绒盖在她身上。这是我的希望。就这样做吧。” 爱玛追求的是激情、冒险、理想化的爱情,而查理则更多是在传统的、安定的框架下展现自己的爱情,他的浪漫是安静的、含蓄的。 我感觉就是他俩不合适彼此,唉……

额外想说的

…别逼自己看自己不感兴趣的书,更别逼自己一口气读完,这个过程真的有点痛苦的……慢点看书,没人催(擦汗)……

 
阅读更多

from senioria

Yep, this is a quite complicated title, so let's illustrate our work in this article with a simple example, in Rust in a different world:

trait DataCap {
    fn serialize(&self) -> String;
}

impl<T: Serialize> DataCap for T {
    fn ser(&self) -> String {
        Serializer::serialize(self, serializer)
    }
}
impl<T: !Serialize> DataCap for T {
    fn ser(&self) -> String {
        unreachable!()
    }
}

Sadly this is not accepted by Rust currently:

  • Specialization is proven unsound, thus would never be stabilized and is not considered;
  • The minimal version of specialization which requires the impls strictly subsetting doesn't apply to this case;
  • Not to mention that negative bounds are far from being added to Rust;
  • Turning to decide the impl during instantiation is against Rust's early error spirit and would never appear in Rust;
  • The lean4 way which just gives up when conflicts and let users to explicit decide which impl should be used is good, but may be too far for industrial langs like Rust :)

Therefore our workaround is to mark whether the trait is implemented, and make a wrapper to unify the impls:

use std::marker::PhantomData;

trait Serialize {
    fn serialize<S>(&self, s: S) -> String;
}

impl Serialize for i32 {
    fn serialize<S>(&self, _s: S) -> String { todo!() }
}

pub trait SerFlag {}
pub struct WithSer;
pub struct NoSer;
impl SerFlag for WithSer {}
impl SerFlag for NoSer {}

// User should impl this
trait DataCap {
    type HasSer: SerFlag;
}
trait DataSer {
    fn serialize(&self) -> String;
}

struct Wrap<T, S: SerFlag>(T, PhantomData<S>);

impl<T: DataCap<HasSer = WithSer> + Serialize> DataSer for Wrap<T, WithSer> {
    fn serialize(&self) -> String { self.0.serialize(()) }
}
impl<T: DataCap<HasSer = NoSer>> DataSer for Wrap<T, NoSer> {
    fn serialize(&self) -> String { "".to_string() }
}

fn use_data<T: DataCap>(data: T) where Wrap<T, T::HasSer>: DataSer {
    let data = Wrap(data, PhantomData::<T::HasSer>);
    data.serialize();
}

impl DataCap for i32 {
    type HasSer = WithSer;
}
impl DataCap for () {
    type HasSer = NoSer;
}

fn main() {
    use_data(0);
    use_data(());
}
 
Read more...

from 漫游者迷路中

⭐️果然看番还是要看自己喜欢的,在月底看的所有番都让我特别开心。果然看不用带脑子的温馨番剧才是我的归宿。

已完结(指动漫)

  • 恶役千金Lv99~我是隐藏BOSS但不是魔王 转生之“我本想低调过一生,为了不被主角团杀,不知不觉练级到99级,举世闻名”。不同于传统的“努力洗白”路线,女主凭着前世的游戏知识,专注提升自己的战斗能力,这点很有意思,不扮猪吃老虎更是一个爽,情感常识缺失也是一种萌点。男女主双向箭头的纯爱也很不错,男主为了追求女主偷偷去练级好磕。而且其实动漫里笔墨没有过多,不多不少刚刚好。是一部轻松搞笑且有点热血(也没太多)的作品,独特的设定和女主的高战力比较亮眼,是值得放松心情时追看的佳作,不过没有深刻的剧情和复杂设定,倒也不用期待这方面。

  • 地下城里的人们 ⭐️顺便一说:2024年与《失忆投捕》获得第二届乐天Kobo电子书“我最想看的作品”类别的大奖。(没人问你) 盗贼少女柯蕾在地下城深处失踪的父亲,在第九层与牛头怪战斗当中,手上的斧头曾不慎使墙塌了!(牛头怪有自己的名字但我忘了)意外得知地下城管理员蓓儿的住处。打不过就加入,成为地下城内的职员。 两个萌妹住一起我就会笑,呵呵我就是这样的人。而且两只萌妹战力都超高这也太萌,就喜欢战力高,爽啊!动漫对地下城的结构和生态进行了细致描绘,包括陷阱维护、怪物分工、资源回收等,融合在两位女主的日常里我觉得超温馨~我对地下城这方面还挺感兴趣的所以也很爱看。 而且我好喜欢小魔像……我喜欢……小魔像……小魔像好可爱嘿嘿·—·

  • 这个世界漏洞百出 这一部可能是我一月看的最有剧情线的一部动漫了(大概)。是以开发中、充满Bug的完全体感型VR游戏为背景的故事。男主作为调试员进入游戏时间,不断记录并报告bug,因为xxxxx,和“一般村民”npc妮可拉一起踏上旅途。但是由于bug调试员们因为bug无法登出游戏,有些人就开始利用权限乱搞了。游戏的“元AI”特斯拉透过借用妮可拉的身体与男主进行交流,要求男主努力除错外,还要想办法夺回那些四处作乱的调试员身上的平板(?)想除去那些为所欲为的愚蠢调试员。(太好了,有意思的剧情因为我的垃圾文笔变得索然无味了) 总之通过利用bug达成的一些演出还是满精彩的。虽然老有人说男主不利用权限是死脑筋、愚蠢,但是我还是很欣赏男主这种坚持自己底线的表现的,有些地方不顾状况的除bug行为也挺贴男主人设的(我觉得) 等待第二季,我超期待的! ⭐️顺便一说:中世纪小女孩就要画的脸圆圆的!!!!!仙品!!!!!

  • 迷宫饭 避开了最热门的时候错峰观看+关弹幕体验感良好。 反正大家都很熟悉迷宫饭了就不简述剧情了。迷宫饭的故事真的蛮厉害的,对各个人物的打磨很强,人物塑造也是比较丰满的。但是!那些自有b站up主解读! 我就喜欢饭啊!!!!看着煮出来好香啊@–@,完全可以想象用现实里哪些食材代替搓出来(搓?)我打算后面好好整理食谱然后自己试着做(有必要吗?!!!)有缘的话我会写一些做饭blog~~就这样画饼!

  • 拥有超常技能的异世界流浪美食家 你们都喜欢取这么长的名字吗?原作名字《网购技能开启异世界美食之旅》也是不短哈! 男主是在召唤勇者时被牵连的普通人,技能是“鸡肋”的网络超市,我只能说你们这些人根本不懂他有多强。男主很聪明拿了一笔钱赶紧跑路,太明智了(点头)。靠美食获得了最强的千年魔兽芬里尔——绯尔,特殊史莱姆个体——史伊,简直是口胃魅魔(乱喊)。绯尔能力超级强,超级帅,但其实是贪嘴二哈;史伊超级萌啊,还很厉害,超级萌啊超级萌啊,想养……男主(你叫什么来着)男主你也很人妻,谢谢你这么会做饭。不过本片打怪是调味,做饭才是正餐!!!! 动漫里做饭的步骤很详细,感觉可以跟着学,所以我也打算有空的时候整理一下食谱学着做……是这样的,我就这样在动漫里学做菜……听说动漫里的广告调味品真的很好用,好好奇,我以后也要买来试试。

  • 舞伎家的料理人 季代与堇因憧憬着成为舞伎于是从青森来到了京都。季代意外成为了宅邸的料理人。在花街这个舞台上,展现了为舞伎们每天做着料理的季代、被称为舞伎中『百年一遇的天才』的堇,以及与她们共同生活的舞伎们的日常。 是一部情感真挚的作品,对于人物塑造非常细腻,通过美食小故事展现了亲情、友情以及舞伎之家的温度,太喜欢本片对与人与人之间情感的描写了,太美好了,时常让我落泪呜呜。中间的小剧场也会插入很多文化背景和美食小知识,很有日本传统文化的魅力,我学习了很多小知识~ 据说真人剧也很好看!!后面打算看看真人剧!!

连载中

⭐️你也能看出我多不喜欢追连载了吧?

  • 中年上班族转生恶役 其实是一月看的第一部番。45岁的上班族大叔灵魂转生到恶役千金身上,其实想扮演好恶役角色,但因已经为人父的慈爱心以及作为公职人员的中年思维而极大的提高了自己的人格魅力(你知道的这是动漫纸片人),原乙女游戏的女主和可攻略人物都对她(他)好感度大UP↑↑↑↑↑ 既有新意又充满潜在的喜剧,看着很开心,也很温馨(我在纸片人方面泪点还挺低…)再多更一集吧!!!我虽然什么都不会做的,但是我很期待!!!

写在最后的话

发现自己剧情真的记不住什么,但是会看得很开心哇!就像我文笔为0但是写得很开心一样哇!!! 而且记住了很多菜式,等我大展身手吧!!

 
阅读更多

from utena

受星友的感染,我也来写一下1月总结,希望这个好习惯可以坚持下来。 文字功底很差,很惭愧地希望观感不会太糟糕。

运动

放寒假之前在stpv发了很多条嘟文说想要打篮球,篮球买了,没有用过。某种程度上是因为不知道怎样开始去做吧。左手运球、右手运球、背后、胯下,是这四个动作轮流去做吗?头脑中没有“篮球练习”的“预演”,模模糊糊,感觉像我很多一时兴起说要做的事一样,跟玩儿似的,有点滑稽。 但是,如果是那种很善于计划的人,一定是这样想的吧。哪怕每天抽出个二十分钟时间,下楼找片空地,有样学样地练一点基本功,三十天过后,应该也不会胆怯了呢。

跑步,没有。没有出门的心情,因为学业的失败而又缩回了壳子里,有这样的因素吧。

饮食

可以说有点灾难了,我真的吃很多甜食和蛋糕,炸物倒是少一点。 我都不敢认真写这一部分。 没有控制支出的意愿。购买食物就是为了及时满足自己的欲望,能有力气面对一下现实,虽然这或许是谎言。

不过,既然是1月总结,还是得全面一些。在盒马买了马苏里拉奶酪和布拉塔奶酪,前者紧实、后者是固液混合的奶包子。我再也不会馋轻食店里售卖的布拉塔了!只是,或许用喷枪烤一烤布拉塔,味道会不错?

睡眠

时不时还是会一两点睡,这两天倒都是十二点左右。 早上都是8-9点起床,起床后洗个热水澡,还是很舒服的。

文娱

14号通了个宵,根据老师划的重点看了几道大题的做法,下午那场试考得还行,这是我抱得最好的一次佛脚。考完回宿舍,从18点昏睡到21点,爬起来复习15号的科目,困得不行,又爬回床上去浅眠。在这样的困意中尝试复习。第二天考完在火车上昏睡。

每次苏醒皆是重生 不再苏醒 便是永恒的死亡 我愿为你编织摇篮 以天鹅绒缝制 灵枢般的摇篮曲 将你包裹

15号晚上我看了Ave Mujica的ep01&02,16号几乎看了一天的MyGO,为了再了解一下Ave Mujica之前都发生过什么。恰巧16号晚上又更新了ep03,看到了令我大受震撼的Mortis玩偶吞噬Mutsumi的场景,以及那段悲伤又绮丽的表演。

接下来的一周简直都在焦急等待ep04和看各种衍生中度过。就没在好好学习。

遇见这部动画还是蛮开心的,可以算是“年轻人的第一部原创番”了,这种大惊小怪假药满天飞的追番体验和心动女嘉宾 Yahata Umiri 女士,为我的一月增添了色彩。听着Ave Mujica和MyGO!!!!!的歌,我也有了发出自己声音的冲动。

之前一度觉得喜欢看MyGO上不了桌(我没有不尊重任何同好的意思啊啊啊。),毕竟是受很多zng追捧的番剧。但我和zng吃一碗饭也不是一天两天了,谁让我之前最喜欢的番剧是Hibike!Euphonium(吹响上低音号)呢。读邱妙津、听绿洲、看大卫林奇的同龄人很酷很有品味,我终究还是个萌废二次元罢了,对着日本动画露出最快乐的笑容。我想这才是正确的自我认知,我走在熙熙攘攘的人群中,窝囊地郁闷着麻木着进食着昏睡着浑浑噩噩度日着,伟大的戏剧在继续,而我贡献不了什么诗,只能写写流水账。

除了MyGO和AM以外,没什么文娱摄入了。

工具

又把YPT捡起来用。毕竟在家整天坐书桌前,基本用电脑,手机就摆一边,处理的事情也很单一,开一下YPT计时很方便。

仍然处在一种低记录欲的状态里,其实写到这里我已经累了,明明还应该好好分析一下自己的各种状态指标的…… 思维方式…… 谬误…… 复盘…… 怎么成长…… 哇啊。

倒是又掏出两个很简朴的平装本子来,一个B5大小的想着是做点书摘陶冶下情操,因为不写的话好像真记不住啊。结果也只有拿出来的那天写了一面,内容来自茨威格的《与魔鬼作斗争》。这本书种草自豆瓣某位大神,说是学习修辞和华丽语言的好范本,但是我目前还在看荷尔德林的部分。一个敏感的诗人,这个形象吸引不了我,这是我的问题吗?

“荷尔德林的思想是天上的流星陨石,而不能像地上采石场的石块一样,被磨光了棱角去砌起一堵生硬的墙。但在魏玛,这个小孩子却让自己坐在费希特和康德的课堂上,笨拙而吃力地研究各种理论学说。”

“……因此,哲学欺骗了这个谦恭的寻觅者。席勒成了枢密官,歌德成了枢密顾问,赫尔德是教会监理会成员,费希特是教授。”

另一个是A5大小的本子,封面上写下了“Ningen ni naritai!!!!!”,内页像清单一样列举了许多想做的事,基本都在stpv提到过,攀岩啊篮球啊之类的。“对着镜子演练亲切的笑容”。

也有那种,方法论句子。“做事靠系统,而不是凭感觉,因为我是一个需要生存下去的INFP”。看着是不是挺搞笑的?反正我至今都没有做到。

Fedi

理论上这个板块该总结一下1月,或者2024年的几个月里,我从与Fedi友们的互动中学到了什么,但是我没力气打字啦,先画个饼吧。

方法论?思维模式?生活哲学?

副小标题:拖延摆烂还有救吗?像我这样可耻地生活着。

我明明已经思考过很多了,可还是忘记。写过再多日记也没有用欸。我可能变得比以前更坏了。

对方法论的理解总是在“充分了解自己 根据自己的特点去做事”和“了解自己要等多久啊 先做起来啊”之间摇摆,然而我们……一向是喜欢折中的,折中的说法就是这两件事可以同时进行,当然是一边积极地采取行动一边又尽可能地了解自己啊。

这样说下来不还是没有用的空话么,任何人看到这篇日志都会觉得博主思维不清吧。所谓的结构化思考到底是什么呢?

思维模式: 你是否保持了一个持续成长的思维方式?有哪些思维习惯促进了进步? 是否有不合适的思维陷阱或偏见? 如何调整你的思维方式,去适应变化?

我想我是没有这个所谓的持续成长的思维方式的,毕竟我停滞不前。

永远没有清晰的思维欸,总是在一片泥泞里缓缓移动着,可能把自己拔出来一点,不继续蠕动的话,又会陷到泥沼里去吧。

尾声

以上,就是1月26日晚上9点的Utena111能写出来的1月总结。

感谢你的阅读🐱没看到这里也一样感谢,毕竟真的很混乱欸。

 
Read more...

from 漫游者迷路中

※因为防止拖延到二月而提前写完了一月的月总结

自身

在24年终总结中我对25年提出了三个期望:①戒掉熬夜;②戒掉对手机的高依赖;③戒掉暴食欲望。 然后在一月份大犯特犯,真正早睡对天数不超过十天,并且大吃特吃。对于手机的依赖稍有减小,这还是因为星露谷立大功,同时使用电脑和平板在油管觅食,由此手机的使用频率就降低了。

运动

运动情况很糟糕,但是还算是坚持了walk-run,以及偶尔的有氧拳击。感觉算不上运动,而是和暴饮暴食进行风险对冲。现在坐久了会自己起来扎马步,是否算得上一种进步呢?

学习

在学习方面可以说是困读 ,在这个过程中困顿、焦虑、效率极低,好在还算是坚持读下来了,读完了四分之三的教材,月底几天就能读完了。这种煎熬感让我实打实恨上了考研……出成绩的时间太晚了,我没有十分的把握能过初试,也没有十分的把握肯定过不了初试,我就像这样被挤压在中间不上不下,安不下心复习也放不下心找工作。不过现在还是复习吧,还有一个月就熬过去了,心情是“想死,但是再看看,之后再死也不迟”。情绪反刍是一种很恐怖的事,不知不觉就陷进去了,积极调整中(但或许只有考上了才能解决,呵呵谁来保佑我)。 闲不下来开始看雅思,但是也是断断续续看看,感觉是高强度备考后没法闲下来的体现。 找fedi网友囤了书单,控制了一下数量,今年应该能看完~ 考虑学日语了,虽然学日语这件事情从我接触二次元开始就在说,但实际上并没有真正进行学习。现在互联网上的资源也比较多了,打算还是把这件事捡起来,多学一门语言,这件事情还是蛮酷的嘛。主要还是因为这样方便,我搞通人,如果我日语学得够用我就去学韩语,我到处学,吃吃吃,当一只书虫。

观鸟

其实高中时常看到我爸出门拍鸟,为我打下了根本不坚定的基础。以前我也断断续续有一些观鸟的行为,今年才算是正儿八经开始系统性的观鸟了。先把我家附近公园的鸟狠狠观赏了!以下为观鸟记录!

  • 白头鹎

特别多的一群小鸟,喜欢在河边玩

  • 白颊噪鹛

很小,躲在河边芦苇里,可惜离得太远了

  • 绿翅短脚鹎

就看到一只,很漂亮的绿色羽毛

  • 红头长尾山雀

红豆汤圆……圆鼓鼓的,小屁股也好可爱……

  • 北红尾鸲

你和楼上红豆汤圆不相上下,也是圆鼓鼓的小肥啾

  • 小䴙䴘

好呆

  • 黑水鸡

有点活泼的呆,呆得很活泼

  • 白鹡鸰

公园那么大,你好喜欢在人道上散步

  • 白鹭

从我头顶飞过去,没拍到

在高空盘旋,生物特征我看到了0个,判断不出来什么品种

还有我们的好伙伴:麻雀……其实还有好几种鸟但是我没看清,也没拍到,鸟鸟就飞了。下次一定把你们都把玩于取景框之间。 以及我的年度(一月)好照片:红头长尾山雀

吉他

练习吉他这件事,手指真的好痛哦!W1的教程跟完了,有了点点进步,现在最大的问题很容易打品,但是又看不太出来到底哪里有问题,左手按弦好像问题不大,等我自己研究研究是拨弦方向问题还是琴颈的问题,自己不敢随便调琴,实在不行找找有没有电吉他的体验课去薅一下老师的知识羊毛。

同人女失败生涯

“别画了,没有人看的♡” 一月份其实没怎么搞忘八,只画了几个Q版呼呼呼!然后开始发誓祈愿:如果考上了全部做成无料到处送(流泪)(如果有人要) 同人文字数为:0。全部在和亲友口嗨,这是一种享受,但如果真要写文就是有点完蛋,文笔很差的我怎敢触碰完美无瑕的空白文档。

博客

关于博客,虽然现在正在写,但我有点见异思迁。看到了网友充满魅力的博客,于是也想整点更花里胡哨的。不过这个是个长期工程,期待未来能顺利完成心中的博客。

MOOD

回想一月过的太快了,心里还是苦苦的就过完了一月份。似乎没有什么好事,怎么都没办法留住快乐的感觉。玩星露谷很开心,玩完就好累好空虚,又会想起自己没看完的书。在家里也很痛苦,好想什么都听不见。出去玩也很难受,好像是一种罪恶。感觉上半年都要这样带着创伤过活,希望至少下半年能快乐!

 
阅读更多

from 漫游者迷路中

阿南年度精选奖

评选标准:不考虑发行时间和体量,不考虑技术力,仅考虑个人感受!我喜欢才是最好的!

最爱玩游戏

提名: ①塞尔达传说-王国之泪 ②动物森友会 ③斯普拉通3 获奖的是↓ ✨动物森友会✨

最佳旅游地点

提名: ①厦门-鼓浪屿 ②平潭岛 ③武夷山 获奖的是↓ 🌊平潭岛🌊

最爱循环音乐

提名: ①李泳知 -Small Girl ②XG-IN THE RAIN ③QWER-run!run!run! ④YOUNG POSSES-ATE THAT 获奖的是↓ 🌧XG-IN THE RAIN🌧

最有效率地点

提名: ①星巴克 ②天台 ③公园 获奖的是↓ ⭐️星巴克⭐️

最爱看视频博主

提名: ①戒社 ②serafinaMAOU ③Airly_ 获奖的是↓ 💗Airly_💗

最爱喝饮品

提名:太多了写不下 获奖的是↓ ☕️库迪-柚见美式☕️

最爱吃的零食

🥢本地魔芋干🥢 好吃会上瘾(流口水)

年度最佳APP

让我们恭喜:YPT 毋庸置疑拿下了这个奖项!!!

阿南24年新接触

失忆投捕:没人告诉我你们打棒球的是这样的人 电吉他:事实上止步于小星星就去备考了(正在重新学习) 星屑:2024年做的最正确的决定——注册了星屑 舞蹈:学了几个月舞蹈!(因为备考终止了)最近要逐步恢复体能继续去练舞!!

阿南24年也有不好的回忆

我才不记录,不好的回忆不许留下来!!!

阿南25年要做什么?

①电吉他精进🎸 ②看书看书看书🎶 ③攻克一下英语📖 ④舞蹈与健身🏃🏻‍♀️

阿南25年要戒掉!

①熬夜 ②对手机的高依赖 ③暴食欲望

 
阅读更多

from 奈芙莲 • 糯可

年终总结

学了很多 C++。其实应该说学了很多计算机语言之类的知识,倒也不只是C++… 这么一说才想起来今年才接触到 Firefish 的开发啊(还见证了 Firefish 就这样死了)。确实学了好多东西。学了 vue 和 ember.js。学了更多 Ruby on Rails,学了更多函数式语法,反正看上去学了好多东西。

感觉 2024 年比起 2023 还是好太多了,至少没有挂那么多科。从今年起开始有动力写作业了。本来是能把作业晾一个学期还懒得点退课键的人。今年至少开始是稍微进入一点正轨了,可惜已经晚了。

我也不知道为什么这么急。或许出生在中国真的是过错,一年的gap都没有,当然gap我也不知道我在哪里怎么活,反正也真心不想被看作没用的人而呆在家里

算了,总之比之前好。

一年多前的创伤经历还是忘不掉,这几年来越发是对于我讨厌的这个身份三缄其口了。有时候也会被那些观念烦到,觉得自己是不是什么忘恩负义或者过了桥就要把桥砍断的人,但是仔细想想我还没过桥啊。而且还是看着一个个人越过我走到了对岸,而我看上去还在桥上,却好像已经溺水了。所以继续心安理得地隐藏自己,反正给我带来的梦魇远多于什么好的东西,不说更好,还能更少激发创伤。

⬆️不过效果似乎太好了点。我时间都记错了,我还以为是22年。原来是23年的事情。

还是没有得偿所愿,也不知道这辈子可不可以,我不想保持着这个样子死去。但如果真的要死了的话其实也谈不上死不瞑目吧,只是真的不想就这样死去。

写着写着就感觉自己能这样活到现在已经很努力了,在旁人眼中摆烂王又有什么关系呢。如果叫我像那些人一样努力的话,总觉得我大概已经死了。

每次写这样的话就觉得很惶恐。我是不是占有了过多的幸运而不自知,我这样的人还过得这么难受是不是对其他人的伤害。但是算了我就是这样的人倒也没错。

对2024的印象就是很单薄,对一个月前的印象也很单薄,感觉不知道为什么自己就这么活过来了。感觉对过去和未来的看法就像裹在迷雾里一样。看向稍微远一点的时间就什么都找不到了。高中的时候还喜欢说完美完成年度计划:活着,明年的计划是活到后年。现在感觉已经说不出来主要是懒得说了,反正活着就像惯性,没人拦着就慢慢匀速直线移动呗,有什么好说的。

也有些好事吧,星屑不知道为什么突然火了。其实完全没想到这样,当初换服务器也只是顺手而为。最开始建星屑的时候,其实并不知道它能不能活到第二年的,没想到它不仅活到了第二年还受到了这么多人的喜欢,感觉很世事无常诶。这样我也有个让自己活下去的理由了,起码我可以为了维护服务器而活着

与此同时某旧日的幻影(不是)留下的论坛就去得越来越稀疏了。它才是真的快要结束了吧,真是可惜了朋友一掷千金(不是)买了八年的服务器。

今年第一次尝试了实习。很新奇的体验。总体来说大概是被夸了,看来我打工写代码的水平还是可以的。只是结束后又感觉有点兴致缺缺,感觉自己已经把从小到大听过的人生必经之路全部走过一遍了。也没有培养过别的爱好。有一种游戏通关以后不想再开了的感觉,虽然在别人眼里看来应该远远没到通关。可能我毕竟懒得收集全成就。

seni 经常对我说很后悔,要是当时拉着我不去管llt的破事的话,或许不会这个样子。我倒是觉得没关系的,一来我从不在上帝视角为过去细心想过的事情后悔,因为再来一次也会做出一样的选择的。二来我爱好太少了,没有什么别的选择去为自己提供一个继续前进的锚点,如果不去做那些别人眼里 time consuming 又useless 但是在我自己眼里是 tasks里面只有这一两个task的事情,会很无聊。我也不知道我怎么就成了这样一个人。

不再玩星穹铁道了。或许哪天无聊我会回去看剧情录屏,但是感觉还是没有动力主动玩下去了。也没有心思去养角色。

总结下来我好像花钱很少。连我妈都说我是貔貅,只进不出。但其实“这能怪我?”.png。如果不是因为从小被灌输了太多赚钱艰难的道理而且直接没有购物自由,哪能养成现在这样扣扣搜搜的性格。

也是今年和同学聊天得知他豪掷3个648只为抽黄泉二命。以为对方是零花钱很多的人,结果没想到其实和我差不多,只是他愿意攒着然后豪掷千金一把。再看看自己每个月生活费都花不完攒着攒着都快有我一个月工资了(喂 什么奇怪言论)(实习一个月就能有五个月生活费 所以其实我实习三个月至少今年能随便花钱了),连60块的大月卡都要犹豫掂量好几天才能下定决心要不要充值。操。我真是穷在心里。

…罢了,起码这说不定能让我在当前世道上多活几年呢?虽然我也不知道多活几年有什么好的。而且我在吃东西上也没亏待过自己,在零食上我也花了不少钱啊。…这下真的貔貅只进不出了吃进嘴里的才是真爱。

感觉暂时没有更多想说的了,好像比较有趣的事情都说过了,那就这样吧。

 
Read more...

from

#YearlyReview

今年是第一次写年终总结,因为之前的几年根本没有兴致写什么年终总结,be like 我这个破破烂烂的人过的破破烂烂的生活有什么好总结的……之前每次新年时就是我最抑郁的时期,在新旧交替之际会尤其地觉得自己的人生过得很差劲。今年真的发生了很多事情,也改变了我很多,我突然发现,我竟然会期待下一年的到来了!这应该是今年最大的改变吧。

今年发生了什么?

签证与经济危机

我写博客的主要想法是记录下可能会提供一些参考的信息,所以想了想还是把自己的经济情况写得详细了一点,所有提到的内容只代表我自己。

最重要的事情要最先说:今年从学生签转为了伴侣签。这也算成功地解决了身份问题吧?主要是,它成功解决了我的经济危机,这真的太救命了。在之前的博客里我简单地提了一下这个事情,去年来荷兰时我身上的所有钱只够两年学费和一年生活费,还有一年的生活费不知道在哪里(总之不在我的钱包里,笑死)

我其实一开始并没有期望我爸妈能给我解决钱的问题,我的计划就是全部都用自己的积蓄出国,这样就算他们不同意也拿我没办法。果然一开始在我提到我要去留学后,我爸妈就不同意,我:反正我已经决定好了,只是通知你们一下而已。他们看我这么坚定,也不说什么了,就变成一直问我:要多少钱、有多少钱、我为什么有钱;而我通通含糊带过,只说我钱不够,还差十万。我爸妈当然是拿不出来了,不过最后他们还是每个月会力所能及地给我转一些钱(三千人民币左右),算是个惊喜之财,感恩🙏

加上了爸妈每个月转的那一些钱,我的经济危机不那么要命了——我当时认真算了算所有自己可以用上的资金以及自己可以节省的程度,结果是应该可以刚刚好极限地cover两年的花费。这个经济状况就像一柄达摩克利斯之剑悬在我头顶,也对我造成了一定的精神压力。我伴侣很想让我放松一些,加上我还遇到了很多各种耗费心力的破事(详情请见前一篇博客),还生了好几场病,只是这个压力不是宽慰几句就可以轻易抹除的。同时因为已经有一些工作经验了,所以我有在积极申请一些freelancer的开发工作,但是结果不太如意,一个都没有找到。此时跟对象的感情十分稳定,再加上欧盟学费实在诱人,我们俩就决定一起把我的伴侣签证办了吧!省略各种闹心的办材料过程,为了满足伴侣签的要求我对象还重新找了份工作,锵锵锵,最后成功地拿到了伴侣签!拿到手后马上跟学校发邮件,成功把两万欧学费变成了两千欧,经济危机就这么解决了。

不知道会不会有人看怒了:可恶,这是能参考的解决方式吗???我望天,假如真的穷到我这个地步,没有一些好运(比如刚好谈到了靠谱的外国伴侣)或者是很多毅力(比如有人每天都靠to good to go存活,一边上学一边打黑工)以外也没啥能解决钱的问题了。

可能这个经济危机的解决方式有点不够励志,我一开始对告诉别人我换成伴侣签了的事有点压力,网上对伴侣签的评价是有点负面的,什么靠男人啊,不独立啊,娇妻啊什么的。以及我也有一点压力担心别人觉得我怎么才去荷兰没多久这么快就找到了对象这么快就办了伴侣签,是不是就是一开始就冲着婚绿才找对象的!!很想解释些什么,但是我又觉得不必自证!而且我也太过于在意别人的看法了吧,会这么想的人应该对我来说也不是什么重要的人,忽略就好了^v^身边亲近的朋友听闻这件事,第一反应是问我恋爱谈得开心吗?感觉这个人是比较确定的可以一起长久陪伴的人了吗?我想了想,肯定地对她们说:是的!我感觉很开心!他真的是个特别好的伴侣!感觉自己在过着一种平静又幸福的生活,我目前还没有看到有什么可能会导致我们分手的🚩red flag。朋友们都很为我高兴,我也很高兴(^з^)-☆

假如我没有对象可以给我转伴侣签,那么我最后应该会通过找兼职(比如打黑工)、省钱不出去玩、尽量自己做饭以及吃便宜食材啊之类的方式来解决。我第一年没有去找兼职是因为学习压力真的太大了,我一年一共修了12门课,每门课都是6学分,我基本每天都有课,并且由于我没有AI相关的基础,所以学得还是有点吃力;但是这样的好处是第二年我的课量就大大减少了,我可以提前毕业或者是用这个时间去找兼职,两者都可以缓解我的经济问题。我算了算,在我没有做兼职、正常吃喝、偶尔会出去玩的情况下,我的平均月花费是916欧,在在荷兰应该算正常地省的花费,自认为没有过得很痛苦,在荷兰这种学习压力下也能比较轻松地坚持,不容易精神崩溃。不然一边兼职和一边上六门课真的太极限了,感觉很容易精神出问题^_^;

如果是用尽各种办法但还差一点钱的话,我会考虑能不能借点钱,或者贷款啊啥的(但是这只适合真的只差一点点了的情况),我总觉得办法总会有的!我爸妈都是底层职业,两个人一起工资都没过万,很多人都想不到这么少的钱要怎么在一线城市养三个孩子,但忽略生活质量不谈,大家也都成功活下来了,这得亏于我爸妈真的是千方百计地节省开销——每次回老家都尽量带很多能放很久的食物;每次都买最便宜的菜(我真的吃番薯叶吃到快吐了,发誓这辈子不会再碰番薯叶),一年内吃牛肉的次数屈指可数;我妈做钟点工的主人家也经常会有很多不要的东西送她(我的衣服和文具很多都是这么来的)等等,数不清啊,从小言传身教呢!

我耳濡目染之下真的觉得怎么样都能活的,总是有办法滴:D 因为相信自己最后能解决钱的问题,所以才就算钱不够也莽撞地直接来了呢。虽然来之前也没意料到自己会这么解决了自己的经济危机,但是就是解决啦。

学业

今年成功地修完了所有课程啦!这表示我只要能写完论文就能毕业了!!我超级无敌开心的!!!其实还想写一个博客介绍我上的这个项目,但是这个项目人不是很多……我们这一批入学的一共就三个中国女生,我一写,那我在互联网上基本就是在裸奔,还是不要了……我的硕士课程一共是120学分,其中必修课程一共36学分,选修课程和毕业论文各42学分。其实我之前没有预料到我学这个硕士课程会这么吃力——我之前上的课都是很纯的cs课,什么操作系统、计算机网络、微机原理、模电数电等等等等,总之没有一门课跟这个AI有太大直接关系。而且我本科的课程设计也没有跟数据分析相关的内容,这个硕士课程也更注重理论一点,所以我刚开始学的时候感觉有点抓瞎:全部听不懂,基础概念全部不会,每次听完课以后还得自己回去把基础概念给补了。我要是喜欢这个方向还好,学着学着我发现其实我更喜欢开发,对AI没啥兴趣,如果人生能重来,我会选隔壁的高性能并发系统方向🚬

前文有提到,我的安排是第一学年里把所有必修课程学完,同时修完大部分选修学分,也就是7门选修课中修完6门。学得真的挺累的,因为荷兰作业全是小组作业,所以不仅要完成作业内容,还有很多跟人沟通协作的内容——我觉得上班都比这个简单,因为上班时同事不会不打一声招呼突然消失退学、领导也不会容忍一个真的什么也不会干的人一直待在自己的团队、同事也不会因为不会/不喜欢这个工作就直接摆烂不做了,反正在我之前待的团队里没有,有也被开除掉了^_^;

目前毕业论文已经有了基本的想法并也已经找好了导师,导师又给我找好了第二导师,两位导师人都很好,感觉自己的毕业论文也可以顺利完成的!

健康

不得不说我自从来荷兰之后真的生了很多病……不太确定是不是因为新冠还没真正过去,我发烧得很频繁。我23年八月到荷兰,十一月发烧了一次;十二月眼睛发炎了,眼皮内部起了个大脓包;十二月底又烧了一次,圣诞节是躺在床上过的。

到了24年,二月中的时候发烧了一次;三月底又烧了一次,这次比较严重,快一周都没退烧,然后四月初去了急诊,医生给了开了很猛的阿莫西林和布洛芬,吃了几天后烧就退了,但是一直咳嗽到了四月底才好全。然后在八月的时候眼睛又发炎了,还是那一个位置,无语啊。九月的时候,一直很乖巧的智齿突然发炎了!我右边的智齿是歪的,总是发炎肿痛,我出国前就去医院把右边两侧智齿给拔了;医生说我左边的智齿长得很好很整齐,可以暂时不用管它,我也看到有些科普文章说长得好好的智齿可以不用拔,我就真的没有拔,没想到今年就被它背刺了!!!去急诊后医生给我开了转介信,也给我开了一些消炎药,让我马上去预约拔牙,反正要等很久,轮到我的时候我的牙齿肯定好了。

医生说得没错,我九月时预约到了十一月初的空位,整整等了两个月!拔完牙后过了一段时间突然过敏严重大爆发(我根本不知道这两者之间有什么联系,可能只是凑巧),我还发嘟嘟说了这个事……首先是眉毛的区域特别特别痒,有点状疹子出现,随后蔓延到整个眼睛部位都肿起来了,肿到眼睛都睁不开的程度,然后脸上也起了很多小疹子,感觉都是非常细小的水疱……然后全身又起了疥螨,全身上下无一不痒,睡都睡不着的程度,所以身上被我挠出了很多疤。GP给我开了很多药,用了以后感觉好了很多,但是感觉现在还没有彻底好,有时候还是会觉得身上痒痒的(不知道是不是痒太久了导致的幻觉)。

其实我觉得主要是长时间的压力过大导致我的身体免疫系统不太好。之前在大厂上班时,我就有全身湿疹的问题了,但是在我辞职后,湿疹就消失了。想跟大家说,皮肤真的对压力水平特别特别敏感,压力会导致体内皮质醇水平升高,从而加重体内的炎症反应。由于我经常会感觉自己对什么过敏,比如说我经常出现皮肤起风团,突然狂流鼻涕等等一些典型的过敏症状,我也曾去医院检查过过敏原,没查出来什么。感觉来荷兰后巨大的学习压力和经济压力让我有点濒临崩溃,毕竟之前在大厂上班时的压力来源还算比较单一(就是工作本身,呵呵),但是在荷兰则是来自四面八方的各种压力,我以为我handle得还可以,回过头来看才发现自己的身体已经在疯狂抱怨了,这么频繁地生病就是它对我最强烈的抗议……没办法,打算明年好好养一养。

旅行

办伴侣签后经济状况真的宽裕了很多,生活质量那是直线上升哇!!!今年夏天就跟对象一起去了西班牙旅游。刚好他要去参加欧洲魔方锦标赛(?大概是叫这个吧,不太懂

西班牙跟荷兰最大的区别就是!它没那么平!!!!我还在西班牙买了个相机,比荷兰便宜一些,开心地用它拍了很多照片!!

西班牙没那么平的证据之一,荷兰在户外一般是见不到这么高的楼梯的、、、

一些西班牙的街道

还拍了一张水果店的照片哈哈哈哈哈哈,大家都说西班牙的水果特别好吃!!但是因为我平时不爱吃水果所以没想起来要去尝一下,流泪

很特别的房子,感觉有日式风格

这个房子的装修也挺特别的,感觉好看就拍下来啦

西班牙的宗教气息比荷兰浓郁一百倍!!拍到了很多漂亮教堂

走路时抬头能看到很多有意思的装饰,比如西班牙人几乎每家都会放一些花盆在阳台上

西班牙还有很多好看的建筑设计,高楼也比荷兰要多呢(也有可能是我待的地方比较村)

在西班牙的最后一天时去了海边!!!因为起得比较晚所以刚好赶上了日落呢(心虚.jpg) 感觉大家在海边的状态都好放松,很悠闲。我就站在那里看着来来往往的人群,听着海浪翻滚的声音,也觉得内心变得无比平静。 最后看完了日落,我们就动身回宾馆收拾东西啦~

今年买了什么?

换做去年,我是没啥能说的,除了必需品以外啥也没买。今年有了很多新玩具,就简单说一下(^з^)-☆

Kobo Clara 2E

今年收到的生日礼物是我拥有的第一台eReader!说实话完全没有买阅读器的计划,一点功课也没有做,所以也完全没有听说过这个牌子。我意外地发现阅读器使用起来比手机阅读确实好不少,今年也用这台阅读器看了一些书,以下是我使用过程中感受到的优缺点:

优点

  • 很轻很便携,我甚至可以把它塞进我的羽绒服口袋
  • 很适合在信号差的火车上阅读,在荷兰经常要坐火车,是个很好的火车伴侣
  • 会自动统计用户的阅读数据,还会预估你大概还需要多长时间看完这本书(在用kepub格式时会自动计算,其他格式没有这个功能)
  • 显示效果不错

缺点

  • 导入书比较麻烦,我一般用数据线连接电脑来批量导(好古老),但是也可以在阅读器里设置网盘的链接,每次联网时它就会自动下载网盘上的最新文件
  • 翻页一般都比较顺畅,但每隔几十页后,下一次翻页就要等比较长的时间加载,不知道为啥
  • 没有翻页键!!!不喜欢没有翻页键,拿着难受
  • 可能是我有几次放太久忘记充电,在休眠状态下断电了,所以阅读记录数据全部丢失了,包括时长统计和看书进度等,我觉得完全可以提供个低电量自动保存数据之类的功能……

阅读统计功能如图所示:

我刚用这个阅读器时也折腾了好长一段时间,最后终于是把它调教成符合我最习惯的用法啦。之前有个想法是把这个换成有翻页按键的阅读器,但是我最想买的Kobo Libra 2已经停产了……我对彩色显示没啥需求,所以就搁置了。

Fujifilm X-T50

这个新相机绝对是今年买到的最让我开心的东西!!!我对摄影最开始的印象是在cosplay时那些摄影的长枪大炮,很让人不明觉厉。我当时很有cosplay的热情,但是面对镜头时会特别僵硬,并且我不太喜欢被摄影品头论足时的那种被凝视感,所以我渐渐对cosplay失去了兴趣,反而会陪亲友出外景时趁机摸摸摄影的相机,从而也知道了一些非常基础的知识。尽管想买相机很多年了,但是直到今年才真正拥有一台自己的相机!

首先决定要买相机后我就在纠结买佳能、尼康、索尼中哪一个,但是最后还是觉得富士的可玩性更高点就决定买富士。做了一些功课后最终选定了这一台,当时在XT50和XT30II之间纠结,最后选了XT50,主要出于几个考虑:

  • 这款相机的操作和配置等比较适合新手入门,但跟XT-5这种旗舰机相机的差距较小,就算新手拥有更专业的摄影知识,对器材有更高的要求后也能继续接着用,又是少有的原价能买到的机型
  • 是可以更换镜头的相机,有机顶闪光灯、高像素、有防抖、对焦和无线连接手机的体验都比XT30II更好,感觉基本满足了我的需求
  • XT30II是三年前的机型了,还是希望买新款机型

想说拥有了自己的相机以后悟了——当模特被别人凝视和自己拿相机去凝视别人这两种感觉真的很不一样诶!我很喜欢捕捉各种美好的瞬间,想拍很多朋友们的照片,想到玩cosplay的时候那群男摄影师一直在拍我的各种腿真的有点恶。。。

但是我当时买这台相机是稍微超了一点点预算的,现在富士出了XM-5,是个更适合新手入门的低预算可更换镜头相机,如果我现在要买自己的第一台相机的话应该会选择它。

Fujifilm XF 16-50mm f/2.8-4.8 R

这只镜头是跟相机一起买的,因为套餐价会比分开买便宜两千块,而且网上对这只新镜头的评价也挺好的,就一起买啦!我挺喜欢这只镜头的,焦段都是很常用的焦段,有个问题就是这只镜头基本只能在白天用……光圈有点太小了。富士不愧是传说中的阳光机,在阳光灿烂时拍到的照片特别特别美!!!!但是在晚上基本就不用想能拍到什么好照片了,只能配合闪光灯使用。下面放了两张照片,分别是白天和晚上的对比。

阿姆斯特丹运河边的一排房子,基本成为了阿姆的代表性标志了

晚上时拍到的照片就变得跟手机差不多……

并且就算是白天拍的照片,在阳光灿烂时和在阴天里拍出来的效果也很不一样。

这是太阳在云层里时拍的:

这是同一天拍的,不过当时太阳比较好:

于是我痛定思痛!下定决心一定要买一只大光圈的镜头!

Viltrox AF 27mm f/1.2 Pro

大光圈镜头来了——拿到以后就被它的重量惊到了,好大一个!比16-50mm那只镜头还要大!给人一种这个镜头真的很有料的感觉(?

我上次跟朋友出去玩带上了这只镜头,天啊这个镜头真的太好用了啊啊啊啊!!!太牛了啊!!!F1.2!就是大!拍人和拍景都表现得非常好,不愧是1.2的大光圈,完全不需要闪光灯,一点点光线就能让我拍出我特别满意的照片55555我真的开心死了!!!好可惜不能给你们看两只镜头拍人像的对比啊,这只镜头真的特别锐,我朋友的头发丝根根可数(?干嘛要数

总之真的非常喜欢这只镜头,感觉以后出去玩会优先带它~但确实有点重,我单手拿它拍照会有点点吃力、、、

放一张广告牌照片:

今年玩了什么?

今年有认真地玩游戏呢!在switch上花了110个小时,在steam上我估计也花了一百多个小时,什么时候steam也可以告诉我今年我在游戏上花了多久啊!!!

在这里不打算列出我玩的所有游戏了,因为我开始沉迷游戏没多久,玩的基本都是家喻户晓的经典大作,,,没啥特别想说的,唯一一个比较小众点的游戏就是Little Kitty, Big City,我很喜欢这款游戏!!这也是我第一个努力玩到了全成就的游戏!!

虽然我已经嘟过了我认为的这个游戏的优缺点,但可能有些人没看到,我就复制粘贴了一下放在了下面哈哈哈哈哈

优点

  • 画风超级可爱,还有个超级萌的帽子收集系统,猫咪带上不同的帽子后加倍可爱!
  • 操作体验感还算可以,会有提示显示你能跳到的位置(非常偶尔会出错),还有不会很轻易地从墙上掉下去之类的
  • 有很多惊喜的小设定(在这里不剧透了,但是真的很激发我的好奇心,会想把所有事都尝试一遍)
  • 互动细节还算丰富,我个人很喜欢

缺点

  • 挺多bug的。根据我自己的体验和网友的反馈,穿模和卡图的bug比较常见 我自己的亲身经历:不小心把一个重要道具掉在了地图边缘,结果怎么都捡不回来了……差点就要建档重玩了,最后我发现往那个物体上跳虽然会被弹回来,但是会使物体移动一点点,然后我就跳了n次把物体挪到了我能够到的地方55555
  • 设定上猫咪可以跳跃的高度有点迷。在游戏里猫咪从楼顶上掉下来都不会死,但是却跳不上围墙……需要借助其他障碍物等才能跳上去(可能是想增加难度吧,但感觉现实中的猫猫完全可以跳得到呀!!)
  • 主线内容比较短。基本上两三个小时就能完成主线任务啦,虽然说还可以继续完成支线任务,但是真的感觉这个体量相比起它60元的价格来说有丢丢小。

由于我晕3D还挺严重的,所以玩游戏的进度比较缓慢——一年过去了,我的林克还在旷野之息里刚打完四大神兽,正在四处各地开启神庙和收集呀哈哈(不知道什么时候才会去打最终boss)

明年想要做什么?

明年的关键词就是休息。我真的太累了,感觉自己像个很努力旋转的陀螺,从来没有停下来过。之前互联网上有个笑话是:中国学生的gap year其实就是抑郁休学。我看到后笑得不行,笑完后又觉得想哭——我高三抑郁症的时候也没休学,硬是一边吃药一边心理咨询,然后考完了高考。得知自己被裁员时,马上就开始学托福准备出国,准备出国的间隙还一边打工给自己赚学费。

细想一下,我真的没有从来都停下来过,可能也是因为知道自己不能停,我身后没有任何可以让我依靠的人,我要是读书不努力我的下场就会跟被嫁给只见了一面的陌生人的堂姐们一样。拿到了伴侣签之后我就开始有想要休息的想法,但我一想到自己要休息一年时,心里却觉得很害怕……我把这种害怕感告诉了朋友,朋友说:“很正常,因为你没休息过所以你会害怕你没经历过的事情,但是等你真的啥也不干了一段时间后就会爱上这种感觉的”,我听了以后,又觉得自己悲惨又觉得很有被安慰到^_^; 因为之前很害怕自己真正地完全闲着,前段时间还投了很多实习,但其实心里并不愿意,但好在由于各种原因实习的事没成(是我的愿力导致的应该),所以!现在是确定了!明年是彻底休息的一年!!!

总之我想做的事情大概就是:

  1. 延迟毕业一年,甚至可能是两年,具体看情况呢
  2. 我有个想做很久了的个人项目的想法,想在明年内把它实现出来
  3. 捡拾起被自己遗弃的爱好们:手工和画画
  4. 玩很多很多的游戏!目前想把未通关的游戏都通关了(死亡搁浅和迪斯科),然后想玩霍格沃茨之遗和博德之门3
  5. 多多运动!锻炼身体!想学游泳!
  6. 多出去玩,拍很多照片——今年只去了西班牙,明年可以去更多地方呀,德国那么近我竟然没去过!

前文也提到过我的身体不太好,经常生病,所以在明年休息时希望能把身体养好一点吧!这么一看想做的事情真的非常多哈哈哈哈哈哈哈!!!明年的年终总结看看自己到底能完成多少件(=´∀`)人(´∀`=)

 
阅读更多

from senioria

This may originate from a quite sophisticated case: we have a list of data, and we want to transform it with a function that may invoke indefinite amount of requests to an web API with rate limit, and is vulnerable of errors. Our goal is to complete as more requests at a time, and invoke as less requests when an error occurs.

Since the transform function is of synchronous logic, i.e. a next request wouldn't be raised until the last request got its response, which prevents us from directly paralleling all requests, which is in fact the default strategy of all request lib, and easy to adjust to fit the rate limit, our parallelism can only take place between different calls to the function, which incurs batching the functions and sync the states like current API usage and errors between the site performing the requests and the site calling the functions.

A natural idea is to bind the two sites, i.e. let the calling site actually call the web API, and the function only returns its arguments. This inverses the common dependency order of the functions. In most languages, such inversion is impossible without coroutines. The resulting architecture is like: the controller polls the coroutines in batch, invoking the requests, providing the results, and fitting other requirements like process the quota or handle the errors.

An implementation in ts may be like:

const data = ...;

const reqlist = [];
let datadone = false;
const poll_co = async ([args, co]) => {
    const { done, value } = co.next(await /* call the API with args */);
    if (!done) reqlist.push([value, co]);
};
do {
    const ps = [];
    for (const _ of Array.from({ length: batchsize })) {
        const req = reqlist.shift();
        if (req) {
            ps.push(poll_co(req));
        } else if (!datadone) {
            const { done, value: co } = data.next();
            if (done) {
                datadone = true;
                break;
            }
            const { done: noreq, value: args } = co.next();
            if (noreq) continue;
            ps.push(poll_co([args, co]));
        } else break;
    }
    try {
        await Promise.all(ps);
    } catch (e) {
        // Process the error
    }
    // Just for simplicity, since we implemented poll_co in this way :)
    await new Promise(res => setTimeout(res, 60000));
} while (reqlist.length != 0);

The idea of the whole article is quite simple... Since with reasonable designs, future is just a special kind of coroutine, and the conversion between coroutine functions and async functions is usually not complex, just incurs some keyword replacement and syntax tweaks. But this is still inevitable if we're refactoring an implement serially execute the transform functions, even for languages like C++ which has a unified coroutine/async system, that the functions alone the whole call chain should be replaced with a new signature, because the type of the function is changed in deed. The actual web API calls are doomed to be replaced one-by-one.

But if the language is effect-based and with a reasonable type system to implicitly propagate the effects (which doesn't exist at all XD), we can omit the chain-replacing step, only have the call-replacing step and the controller part, since the added effect would just implicitly infect the functions alone the chain.

Well... So this is only an article showcasing one of the benefits of algebra effects: for implicitly-propagated effects, only the use site and the handle site needs to be focused, all other layers would automatically adapt to them.

 
Read more...

from 漫游者迷路中

总之莫名奇妙地写了

游戏机制

在这个生存游戏中,玩家需要管理生命值(HP)、资源(如食物和材料)、进行探索和战斗,最终获得通信装置成功逃离。

关于HP

HP≤0玩家死亡 探索:一次消耗20HP,战斗是随机伤害 食物:香蕉:每个恢复 10 HP;烤鱼:每个恢复 50 HP;烤肉:每个恢复 100 HP 注意保持 HP 在安全范围内是顺利活下去的关键哦!

探索与事件

每次探索会消耗一定的 HP,并有一定概率遇到敌人或获得物品。

探索中可获得的资源

  • 1. 食物:香蕉。
  • 2. 材料 石头:用于制作尖石头与炉子。 粗树枝:用于制作石矛、钓鱼竿。 树藤:用于制作石矛和钓鱼竿。 落叶:用于烹饪食物。 粗骨头:用于制作骨矛。 尖骨头:用于制作骨矛。 收音机、发电机:用于制作通信装置。
  • 3. 敌人 在探索过程中,玩家可能会遇到敌人(如兔子、鹿和熊),击败这些敌人后可以获得以下资源: 兔子(50HP):掉落尖骨头、生肉和落叶。 鹿(80HP):掉落粗骨头、生肉和粗树枝。 熊(150HP):掉落粗骨头、生肉、香蕉、尖骨头和发电机。 》兔子的攻击力范围是 10 到 30。 》鹿的攻击力范围是 20 到 50。 》熊的攻击力范围是 50 到 100。 》赤手空拳攻击力范围是10到30。

制作与烹饪

1. 武器和工具

【石矛】(-30~50HP) 需求:2 个粗树枝、1 个尖石头、1 个树藤 用途:用于攻击敌人。 【骨矛】(-40~60HP) 需求:2 个粗骨头、3 个尖骨头 用途:更强的武器。 【钓鱼竿】(一次性获得) 需求:2 个粗树枝、2 个树藤、1 个尖骨头 用途:用于钓鱼。 【炉子】(一次性获得) 需求:5 个石头 用途:用于烹饪食物。

2.钓鱼

很简单就一句话概况,可以获得:生鱼(1 个);生鱼(2 个);生鱼(3 个);落叶;尖骨头;信号发射器

3.烹饪

【烤鱼】 需求:1 个生鱼、5 个落叶 恢复 HP:50 【烤肉】 需求:1 个生肉、1 个粗树枝、5 个落叶 恢复 HP:100

游戏策略(个人向)

生存游戏里最重要的是存活,由于这个游戏没有所谓的血上限,所以及时补充血量非常重要。 由于探索一天就要减去二十的HP,因此只要一天内没有获得两根香蕉HP是一定会下降的。过于倒霉的情况下建议重开。

游戏初期主要靠香蕉维持血量,遇到动物先逃跑。根据动物的攻击力加上如果玩家非常非常倒霉,建议HP保持在100以上再开始打兔子,但一般而言70HP已经够了。

总之前期尽量避战,收集粗树枝,树,藤尖,骨头,石头等,尽早制作钓鱼竿和炉子。钓鱼竿可以帮助获取生鱼,而炉子可以用来烹饪食物,提高食物的恢复效果。有了烤鱼可以大幅度提升玩家活着的概率。

及时制作武器,基本上有了石矛之后,都能较为简单地捕猎鹿和兔子。这个时候,也不建议和熊进行对战,因为熊的暴击高达一百HP很容易暴毙。和熊对战,至少要血量到达400HP,并且拥有骨矛之后再进行对战才比较安全。

石矛和骨矛都有耐久,所以为了安全请尽量保持在2个以上。 到这里基本上生存已经没有什么问题了,因为在这个过程中,玩家还会不断地获取香蕉,加上烤鱼和烤肉补充的HP幅度很大,血量会越来越厚。这个时候,就要开始有意识地收集过关材料了。

探索获得收音机(较简单) 靠钓鱼获得信号发生器(一般):建议在血量溢出的时候集中钓鱼。我个人感觉这样子获得的概率大一点。 与熊对战获得发电机(难):因为遇到熊的概率就低,掉落发电机概率更低,加一起低上加低,所以后期能保证存活的情况下遇到熊就打。

如何加速逃离:个人是维持血量在550以上(防止暴毙),低于550才会打兔子和鹿获取生肉,得到大量生肉进行烹饪(这个时候也有很多香蕉了),回复HP至1000+,再不停探索,遇见兔子和鹿直接逃跑。

关于卡顿:玩到后面好卡,我会将背包里的大量石头制作尖石头然后制作石矛,这样会好一点,但不知道为什么。总之消耗掉东西就好好一点。

 
阅读更多

from 奈芙莲 • 糯可

前文略,总而言之这篇笔记将告诉你如何迁移你的 Firefish 到 Sharkey.

准备工作

在接下来的操作前,请务必备份你的数据库。 迁移前请务必备份你的数据库,因为这是一个非常非常危险的操作,一旦失误会弄坏你的数据库。

请先在本地测试环境中按该笔记进行操作,再在生产环境操作。 原因同上。

本教程基于 Manual 安装而非 Docker 安装,如果你使用的是 Docker 安装,关于数据库的操作很可能有区别。抱歉我无法提供这方面的帮助,请自行将下面的命令翻译为 Docker 上的数据库操作。

备份

如果你要正式开始迁移工作,请务必在备份前停止 Firefish。 但如果你只是现在本地环境中进行测试,就不必了。

使用 pg_dump 将你的数据库进行备份。命令大约如下:

pg_dump {your_database_name} -f {backupname}.sql

例如,如果你的数据库名是 firefish,可以这样:

pg_dump firefish -f firefish_20240906.sql

对于整篇笔记,我们假定您的数据库名是 firefish 。数据库用户名也是 firefish, 密码是 firefish_pswd。请在下文自行替换对应名称。

数据库的用户名和密码可以在 Firefish 目录下的 .config/default.yml 找到

你可以考虑在导出 sql 的时候不导出权限和用户名,这样可以方便迁移到其他用户和其他数据库名上。否则,如果你不是原地迁移。后面的迁移可能会遇到权限问题。如果遇到了这样的问题,你可以看看 附录:权限错误与解决方法

  • --no-owner:不导出对象所有者信息。
  • --no-privileges--no-acl:不导出对象的权限(访问控制列表)。

还原

如果您在本地测试环境,或者其他服务器中尝试接下来的步骤,您现在已经可以将您的备份文件下载到对应环境了。

或者,如果您的 Firefish 还在使用 Postgres 12 甚至以下,数据库的版本已经过旧,您可以趁机升级一下数据库版本。

您接下来可以使用这样的命令恢复数据库:

psql postgres

进入 postgres。

如果你是还原备份,此时已经存在 firefish 数据库,请把它删掉:

DROP DATABASE firefish;

此后,执行:

CREATE DATABASE firefish;
exit

创建 firefish 数据库。

现在,执行这样的命令:

psql -U {your_username} -d {your_database} -f {your_backup}.sql

例如,对 firefish 用户还原 firefish 数据库,你可以:

psql -U firefish -d firefish -f path/to/backup.sql

降级

该内容来自 Firefish 官方降级文档,有使用自己经验的修改。

升级你现在使用的 Firefish 版本到最新版本。执行 pnpm run migrate 后在 firefish 软件的根目录原地进行此操作:

psql --file=docs/downgrade.sql --user=your_user_name --dbname=your_database_name

例如,本案例中是

psql --file=docs/downgrade.sql --user=firefish --dbname=firefish

If you get the FATAL: Peer authentication failed error, you also need to provide the --host option (you will be asked the password):

psql --file=docs/downgrade.sql --user=your_user_name --dbname=your_database_name --host=127.0.0.1

因为你是要迁移到 Sharkey,成功执行到这里后就不必按官方降级文档的继续了。到这里,你可以对 Firefish say bye-bye了,后面不再需要用到它。

安装 Sharkey 仓库

选你中意的位置,跳过新建用户的步骤(因为你的 Firefish 已经有用户了!)安装 Sharkey 直到 initialize 数据库的步骤:

git clone --recurse-submodules -b stable https://activitypub.software/TransFem-org/Sharkey.git
cd Sharkey
pnpm install --frozen-lockfile
cp .config/example.yml .config/default.yml

编辑 .config/default.yml 使其与你的 Firefish 使用相同的数据库

vim .config/default.yml

Build

pnpm run build

接下来按照迁移说明进行操作,这里为了 Manual install 做了修改:

psql 到你的 firefish 数据库,执行这样的 SQL:

-- start a transaction, so we won't leave the db in a halfway state if
-- things go wrong
BEGIN;

-- we need to add back some columns that Firefish removed, but that
-- Sharkey migrations expect
ALTER TABLE "user_profile" ADD "integrations" JSONB NOT NULL DEFAULT '{}';
ALTER TABLE "meta" ADD "twitterConsumerSecret" VARCHAR(128);
ALTER TABLE "meta" ADD "twitterConsumerKey" VARCHAR(128);
ALTER TABLE "meta" ADD "enableTwitterIntegration" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "meta" ADD "enableGithubIntegration" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "meta" ADD "githubClientId" VARCHAR(128);
ALTER TABLE "meta" ADD "githubClientSecret" VARCHAR(128);
ALTER TABLE "meta" ADD "enableDiscordIntegration" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "meta" ADD "discordClientId" VARCHAR(128);
ALTER TABLE "meta" ADD "discordClientSecret" VARCHAR(128);

-- also an extra table, for the same reasons
CREATE TABLE antenna_note();

-- Misskey used to have a Reversi game, Firefish dropped the tables,
-- now Misskey uses them again
CREATE TABLE "reversi_game" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "startedAt" TIMESTAMP WITH TIME ZONE, "user1Id" character varying(32) NOT NULL, "user2Id" character varying(32) NOT NULL, "user1Accepted" boolean NOT NULL DEFAULT false, "user2Accepted" boolean NOT NULL DEFAULT false, "black" integer, "isStarted" boolean NOT NULL DEFAULT false, "isEnded" boolean NOT NULL DEFAULT false, "winnerId" character varying(32), "surrendered" character varying(32), "logs" jsonb NOT NULL DEFAULT '[]', "map" character varying(64) array NOT NULL, "bw" character varying(32) NOT NULL, "isLlotheo" boolean NOT NULL DEFAULT false, "canPutEverywhere" boolean NOT NULL DEFAULT false, "loopedBoard" boolean NOT NULL DEFAULT false, "form1" jsonb DEFAULT null, "form2" jsonb DEFAULT null, "crc32" character varying(32), CONSTRAINT "PK_76b30eeba71b1193ad7c5311c3f" PRIMARY KEY ("id"));
CREATE INDEX "IDX_b46ec40746efceac604142be1c" ON "reversi_game" ("createdAt");
CREATE TABLE "reversi_matching" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "parentId" character varying(32) NOT NULL, "childId" character varying(32) NOT NULL, CONSTRAINT "PK_880bd0afbab232f21c8b9d146cf" PRIMARY KEY ("id"));
CREATE INDEX "IDX_b604d92d6c7aec38627f6eaf16" ON "reversi_matching" ("createdAt");
CREATE INDEX "IDX_3b25402709dd9882048c2bbade" ON "reversi_matching" ("parentId");
CREATE INDEX "IDX_e247b23a3c9b45f89ec1299d06" ON "reversi_matching" ("childId");

-- move aside some FireFish columns; Sharkey migrations will
-- re-create them; we don't `DROP` them because we want to keep the data
ALTER TABLE "user" RENAME COLUMN "movedToUri" TO "ff_movedToUri";
ALTER TABLE "user" RENAME COLUMN "alsoKnownAs" TO "ff_alsoKnownAs";
ALTER TABLE "user" RENAME COLUMN "isIndexable" TO "ff_isIndexable";
ALTER TABLE "user" RENAME COLUMN "speakAsCat" TO "ff_speakAsCat";
ALTER TABLE "user_profile" RENAME COLUMN "preventAiLearning" TO "ff_preventAiLearning";
ALTER TABLE "meta" RENAME COLUMN "silencedHosts" TO "ff_silencedHosts";

-- this column was added by both Firefish and Misskey, but with
-- different names, let's fix it
ALTER TABLE "meta" RENAME COLUMN "ToSUrl" TO "termsOfServiceUrl";

-- update antenna types, this is only needed on some instances but
-- recommend to run anyway
--
-- this *removes* any antennas of types not supported by Sharkey!
CREATE TYPE public.new_antenna_src_enum AS ENUM ('home', 'all', 'list');
ALTER TABLE antenna ADD COLUMN new_src public.new_antenna_src_enum;
DELETE FROM antenna WHERE src NOT IN ('home', 'all', 'list');
ALTER TABLE antenna DROP COLUMN src;
ALTER TABLE antenna RENAME COLUMN new_src TO src;
DROP TYPE public.antenna_src_enum;
ALTER TYPE new_antenna_src_enum RENAME TO antenna_src_enum;

-- optional but recommended: delete all empty moderation log entries
DELETE FROM moderation_log WHERE info = '{}';

-- only needed on some instances, run this if
-- `\dT+ user_profile_mutingnotificationtypes_enum`
-- does not show `note` in the "elements" section
ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum" ADD VALUE 'note';

如果有任何报错信息,请停止迁移!除非你真的知道自己在干什么,不要乱动数据库。去 Sharkey 提供的 Matrix or Discord 求助。

如果没有报错,你就可以接着输入

COMMIT;

提交这些变更。

好了,现在你可以启动 Sharkey 的 migrations 了。在 Sharkey 的目录:

pnpm run migrate
pnpm run start

如果两条命令都没有报错的完成了,并且 Sharkey 说它正在监听端口, Ctrl+C 关闭 Sharkey。现在,还要对数据库进行一些小小的按摩(?)

仍然是

psql firefish

到你的数据库:

BEGIN;

-- all existing users are approved, because Firefish doesn't have a
-- concept of approvals
UPDATE "user" SET approved = true;

-- now we put back the data we moved aside
UPDATE "user" SET "movedToUri" = "ff_movedToUri" WHERE "ff_movedToUri" IS NOT NULL;
UPDATE "user" SET "alsoKnownAs" = "ff_alsoKnownAs" WHERE "ff_alsoKnownAs" IS NOT NULL;
UPDATE "user" SET "noindex" = NOT (COALESCE("ff_isIndexable", true));
UPDATE "user" SET "speakAsCat" = COALESCE("ff_speakAsCat", false);
UPDATE "user_profile" SET "preventAiLearning" = COALESCE("ff_preventAiLearning", true);
UPDATE "meta" SET "silencedHosts" = COALESCE("ff_silencedHosts",'{}');

ALTER TABLE "user" DROP COLUMN "ff_movedToUri";
ALTER TABLE "user" DROP COLUMN "ff_alsoKnownAs";
ALTER TABLE "user" DROP COLUMN "ff_isIndexable";
ALTER TABLE "user" DROP COLUMN "ff_speakAsCat";
ALTER TABLE "user_profile" DROP COLUMN "ff_preventAiLearning";
ALTER TABLE "meta" DROP COLUMN "ff_silencedHosts";

如果没有报错,你就可以接着输入

COMMIT;

提交这些变更。

现在,Sharkey的迁移已经完成了。你可以继续 Sharkey 的启动,比如配置 Systemd 项目。如果你之前配了 S3, 请去管理面板查看一下这些设置项,因为可能会迁移出类似这样的 URL: https://https://yourdomain.com, 你可以修复它。

附录:权限错误与解决方法

通常发生于备份文件的用户名、数据库名、权限和你的数据库内的设置不一致的时候。

以下SQL全部需要 psql firefish (或对应的数据库名)后使用。需要将 your_user 替换成你的用户名。

error: 对表 xxx 权限不够

直接把 firefish 数据库内的所有表格权限授予给你的用户:

GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO your_user;

然后将所有表格的owner设置成你的用户

DO $$
DECLARE
  r RECORD;
BEGIN
  FOR r IN
    SELECT table_schema, table_name
    FROM information_schema.tables
    WHERE table_schema NOT IN ('pg_catalog', 'information_schema') AND table_type = 'BASE TABLE'
  LOOP
    EXECUTE 'ALTER TABLE ' || r.table_schema || '.' || r.table_name || ' OWNER TO your_user';
  END LOOP;
END;
$$;

必须是类型 notificationtypeenum 的属主

同样,将数据库中所有自定义类型的所有者更改为你的用户

DO $$
DECLARE
  r RECORD;
BEGIN
  FOR r IN
    SELECT n.nspname AS schema_name, t.typname AS type_name
    FROM pg_type t
    JOIN pg_namespace n ON n.oid = t.typnamespace
    WHERE t.typowner <> (SELECT oid FROM pg_roles WHERE rolname = 'firefish')
      AND n.nspname NOT IN ('pg_catalog', 'information_schema')
      AND t.typtype = 'e'  -- 仅针对枚举类型(自定义类型)
  LOOP
    EXECUTE 'ALTER TYPE ' || r.schema_name || '.' || r.type_name || ' OWNER TO your_user';
  END LOOP;
END;
$$;

附录:作者修订

用户数和帖子数一直显示 0

迁移后,有概率出现用户数和帖子数一直显示 0 的问题。这时可以进入后台作业队列,如果发现像这样的错误:

null value in column "id" of relation "__chart_xxxxxx" violates not-null constraint

可以去 psql 进入数据库。尝试输入:

\d __chart__federation

它理应返回

                                              数据表 "public.__chart__federation"
               栏位               |        类型         | 校对规则 |  可空的  |                      预设
----------------------------------+---------------------+----------+----------+-------------------------------------------------
 id                               | integer             |          | not null | nextval('__chart__federation_id_seq'::regclass)
 date                             | integer             |          | not null |
 unique_temp___deliveredInstances | character varying[] |          | not null | '{}'::character varying[]
 ___deliveredInstances            | smallint            |          | not null | '0'::smallint
 unique_temp___inboxInstances     | character varying[] |          | not null | '{}'::character varying[]
 ___inboxInstances                | smallint            |          | not null | '0'::smallint
 unique_temp___stalled            | character varying[] |          | not null | '{}'::character varying[]
 ___stalled                       | smallint            |          | not null | '0'::smallint
 ___sub                           | smallint            |          | not null | '0'::smallint
 ___pub                           | smallint            |          | not null | '0'::smallint
 ___pubsub                        | smallint            |          | not null | '0'::smallint
 ___subActive                     | smallint            |          | not null | '0'::smallint
 ___pubActive                     | smallint            |          | not null | '0'::smallint
索引:
    "PK_b39dcd31a0fe1a7757e348e85fd" PRIMARY KEY, btree (id)
    "IDX_36cb699c49580d4e6c2e6159f9" UNIQUE, btree (date)
    "UQ_36cb699c49580d4e6c2e6159f97" UNIQUE CONSTRAINT, btree (date)

注意预设中的 nextval('__chart__federation_id_seq'::regclass)

你很可能在迁移中丢失了该预设。因此,你可以在 Sharkey 已经可以运行后尝试执行:

BEGIN;
ALTER TABLE public.__chart__active_users ALTER COLUMN id SET DEFAULT nextval('__chart__active_users_id_seq'::regclass);
ALTER TABLE public.__chart__ap_request ALTER COLUMN id SET DEFAULT nextval('__chart__ap_request_id_seq'::regclass);
ALTER TABLE public.__chart__drive ALTER COLUMN id SET DEFAULT nextval('__chart__drive_id_seq'::regclass);
ALTER TABLE public.__chart__federation ALTER COLUMN id SET DEFAULT nextval('__chart__federation_id_seq'::regclass);
ALTER TABLE public.__chart__hashtag ALTER COLUMN id SET DEFAULT nextval('__chart__hashtag_id_seq'::regclass);
ALTER TABLE public.__chart__instance ALTER COLUMN id SET DEFAULT nextval('__chart__instance_id_seq'::regclass);
ALTER TABLE public.__chart__network ALTER COLUMN id SET DEFAULT nextval('__chart__network_id_seq'::regclass);
ALTER TABLE public.__chart__notes ALTER COLUMN id SET DEFAULT nextval('__chart__notes_id_seq'::regclass);
ALTER TABLE public.__chart__per_user_drive ALTER COLUMN id SET DEFAULT nextval('__chart__per_user_drive_id_seq'::regclass);
ALTER TABLE public.__chart__per_user_following ALTER COLUMN id SET DEFAULT nextval('__chart__per_user_following_id_seq'::regclass);
ALTER TABLE public.__chart__per_user_notes ALTER COLUMN id SET DEFAULT nextval('__chart__per_user_notes_id_seq'::regclass);
ALTER TABLE public.__chart__per_user_pv ALTER COLUMN id SET DEFAULT nextval('__chart__per_user_pv_id_seq'::regclass);
ALTER TABLE public.__chart__per_user_reaction ALTER COLUMN id SET DEFAULT nextval('__chart__per_user_reaction_id_seq'::regclass);
ALTER TABLE public.__chart__test ALTER COLUMN id SET DEFAULT nextval('__chart__test_id_seq'::regclass);
ALTER TABLE public.__chart__test_grouped ALTER COLUMN id SET DEFAULT nextval('__chart__test_grouped_id_seq'::regclass);
ALTER TABLE public.__chart__test_unique ALTER COLUMN id SET DEFAULT nextval('__chart__test_unique_id_seq'::regclass);
ALTER TABLE public.__chart__users ALTER COLUMN id SET DEFAULT nextval('__chart__users_id_seq'::regclass);
ALTER TABLE public.__chart_day__active_users ALTER COLUMN id SET DEFAULT nextval('__chart_day__active_users_id_seq'::regclass);
ALTER TABLE public.__chart_day__ap_request ALTER COLUMN id SET DEFAULT nextval('__chart_day__ap_request_id_seq'::regclass);
ALTER TABLE public.__chart_day__drive ALTER COLUMN id SET DEFAULT nextval('__chart_day__drive_id_seq'::regclass);
ALTER TABLE public.__chart_day__federation ALTER COLUMN id SET DEFAULT nextval('__chart_day__federation_id_seq'::regclass);
ALTER TABLE public.__chart_day__hashtag ALTER COLUMN id SET DEFAULT nextval('__chart_day__hashtag_id_seq'::regclass);
ALTER TABLE public.__chart_day__instance ALTER COLUMN id SET DEFAULT nextval('__chart_day__instance_id_seq'::regclass);
ALTER TABLE public.__chart_day__network ALTER COLUMN id SET DEFAULT nextval('__chart_day__network_id_seq'::regclass);
ALTER TABLE public.__chart_day__notes ALTER COLUMN id SET DEFAULT nextval('__chart_day__notes_id_seq'::regclass);
ALTER TABLE public.__chart_day__per_user_drive ALTER COLUMN id SET DEFAULT nextval('__chart_day__per_user_drive_id_seq'::regclass);
ALTER TABLE public.__chart_day__per_user_following ALTER COLUMN id SET DEFAULT nextval('__chart_day__per_user_following_id_seq'::regclass);
ALTER TABLE public.__chart_day__per_user_notes ALTER COLUMN id SET DEFAULT nextval('__chart_day__per_user_notes_id_seq'::regclass);
ALTER TABLE public.__chart_day__per_user_pv ALTER COLUMN id SET DEFAULT nextval('__chart_day__per_user_pv_id_seq'::regclass);
ALTER TABLE public.__chart_day__per_user_reaction ALTER COLUMN id SET DEFAULT nextval('__chart_day__per_user_reaction_id_seq'::regclass);
ALTER TABLE public.__chart_day__users ALTER COLUMN id SET DEFAULT nextval('__chart_day__users_id_seq'::regclass);

为所有的表格重新设置预设。 如果没有错误提示,则可以 COMMIT; 提交这些更改。等待一天,帖子数和用户数就会重新显示了。

 
阅读更多