阅读视图

发现新文章,点击刷新页面。

机器人下鱼塘、救膝盖?这场机器人“脱口秀”路子太野了

鱼塘机器人?迷你皮卡?膝盖外骨骼?

看惯了这一年里,人形机器人只有“进工厂、进家庭、无所不能”这一种宏大叙事;也听腻了各种研讨会上,大家西装革履地端着,一脸严肃地分析“行业卡点”在哪里。

不得不说,相比之下,笔者最近去的一场机器人脱口秀,画风实在太清新了。

在这里,你看到有人为了解决怎么在鱼塘里搞自动化,直接把机器人“开”进了水里,搞出了水产养殖机器人; 在这里,有人根本不在乎什么工业革命,纯粹是因为自己去青藏高原徒步“膝盖疼”,就硬磕出了一套重装徒步外骨骼; 甚至还有人直言不讳,说自己做AI健身教练起家,核心技术竟然是“怎么靠忽悠,骗用户多流点汗”。

这一幕幕,就发生在11月21日下午,地瓜机器人主办的“地心引力”派对。

没有冗长的开场白,十位背景迥异的开发者轮番登台——他们之中,有刚拿融资的连续创业者,有大厂转身的技术高管,有还在象牙塔里造梦的00后学生,也有深耕十年的行业老炮。大家抛开客套,只聊自己手里那个“正在生长”的机器人。

看惯了正襟危坐的研讨会,这些看似“离谱”、实则“生猛”的故事,让我们看到了中国具身智能最真实的样子。

 

当机器人“入侵”鱼塘与荒野

 

机器人到底离改变我们的生活有多远?

在各种高大上的研讨会上,大家总有各种各样的担忧:现在的机器人是不是太脆弱了?还需要多少年才能走出实验室?但在地瓜机器人的这场派对中,最迷人的地方在于,大家的共识惊人的一致:不需要等未来,机器人现在就能改变生活。

机器人被一群胆大包天的开发者扔进了泥泞、荒野,甚至是你周末的露营地里,去解决那些真实得有点千奇百怪的痛点。从一个很简单的初心,到一个真正的产品成型,机器人也可以是这样的。

机器人,可以是为了解决父辈的心酸。

来自塘前燕机器人的创始人韩冬,上台没讲算法,先讲了自己的身份——一个“鱼塘娃”。

当大多数人还在卷人形机器人如何优雅地走进家庭时,韩冬想用自己的能力,解决从小看到父辈们经历的辛酸。他发现,水产养殖虽然产值巨大,却是自动化的“绝对洼地”。

“养鱼就像赌博。”韩冬在现场说道。鱼塘是一个极其复杂的生物系统:天气的变化、甚至一只鸟飞过留下的粪便,都可能带来病毒,引发系统性崩盘。

于是,他决定做一个鱼塘机器人。他把一位做自动驾驶的朋友、拉下了水,两个人从研究千万级的自动驾驶订单,变成了天天盯着鱼塘发呆。

为了在这个从没有自动化的领域做出智能机器人,他们花费无数的心思去从零开始积累数据。一代鱼塘娃,长大了,可以用机器人去守住父辈们最不确定的收成了。

机器人,也可以是纯粹因为膝盖疼而长出来的执念。

无待动力的创始人毛榉一上台,画风瞬间变成了一场荒野求生电影。

他分享的起点不是什么宏大的商业计划书,而是大三休学去青藏高原的一次重装徒步。那是一次绝望的旅程:原本计划骑马穿越祁连山,结果因为荒野里前后十几公里都没有草,马儿“罢工”了。于是,浪漫的骑马穿越变成了残酷的“人背马”重装徒步。

在那几天的绝望中,他切身体会到了真正的痛点——徒步时上下坡膝盖承受的巨大冲击,那种痛感让人根本无心欣赏眼前的美景。“市面上的外骨骼为什么没用?因为它们大多是针对髋关节的,只能帮你在平地上把腿抬起来。下坡要减速,要保持,所以你的肌肉一直在撕裂……这都是膝关节主导的。”

为了对抗物理意义上的“地心引力”,为了让自己和驴友们在看风景时不再因为身体痛苦而分心,他硬是磕出了一款不做髋关节、只死磕膝盖痛点的外骨骼。这不是为了风口而造的产品,这是从骨头缝的痛感里长出来的真实需求。

机器人,还可以用来温柔地修补这个世界的缺憾。

如果说前两者是在解决物理世界的难题,睿亚极光的Steve,则是在用最硬核的技术做最温柔的事——把NASA的技术用在盲人身上。

Steve的师承背景非常“硬”——他的导师来自NASA JPL(喷气推进实验室),研究的是太空级别的MEMS与AI融合技术。但Steve没有选择去做航天,而是把这种原本服务于火星探测的“高维技术”,带回了地面,甚至带到了最边缘的群体——盲人身边。

“在太空里算力有限,所以传感器必须极致高效;盲人的世界也是一样。”Steve展示的为盲人做了一个最方便的感知设备。

在内蒙古,有机构利用他们的设备,训练出了专门识别路边“电线杆”的模型,帮助盲人避开这些危险的“隐形杀手”。他的技术被微软CEO纳德拉点赞,现在也在服务于残特奥会的盲人运动员。Steve用行动证明了一种极客的浪漫:最顶级的技术,不一定非要去征服星辰大海,也可以低下头,去照亮一部分人脚下的路。

机器人到底该往何处去?从泥泞的鱼塘,到缺氧的高原,再到盲人脚下的路,这些开发者没有被“高大上”的概念束缚。在这场派对上,笔者看到一群开发者用自己的作品在说:只要有痛点的地方,就是机器人野蛮生长的土壤。

 

新老“造物者”的疯狂对撞

 

另一个让笔者惊讶的是,在这场派对上,“造物者”的画像变得极其丰富。具身智能不再是象牙塔里的概念,它正在笼络各种各样的人——有深沉的行业老兵,有狡黠的连续创业者,也有还在象牙塔里的00后学生。

网境智能的万琪State Labs的宋军就是行业老兵的典型代表。在做具身智能之前,这两位创始人都在各自的领域(云计算、AR/VR等)有着极深的积累。

转战具身智能后,他们不约而同地盯上了落地最前沿、也最棘手的问题——大脑(模型)和本体(硬件)之间该如何连接?

万琪盯上了机器人的那只“手”。

他运用之前在空间视觉领域的积累,做了一个自带高频空间感知和轨迹规划能力的智能夹爪(Gripper)。

在传统的具身智能领域,机器人想要知道手在哪里,往往要靠昂贵的、刚性极强的本体(六轴+关节)来一层层死算末端位置。这不仅需要大量算力,对硬件的一致性要求极高。而万琪直接做出了一个自带“独立小脑”的夹爪——不需要管胳膊怎么动,这只手自己能看、自己能算、自己能精准定位。

这一刀,精准切掉了行业里两个“最不性感但最要命”的成本痛点:

  • 一是售前成本: 以前为了保证机械臂的精度,出厂前要跑三个月的疲劳测试;现在末端能自己定位,这三个月的时间直接省了。
  • 二是售后成本: 机器人跑久了关节会漂移、螺丝会松动。以前这就得让工程师上门修,现在根本不需要——哪怕本体松了个螺丝,这只聪明的“手”也能自己修正误差,把活儿干了。

这极大降低了人形机器人和机械臂的交付门槛。

而宋军则盯上了“模型不懂硬件”的鸿沟。

有着底层计算架构背景的他,正在解决分布式计算的难题。现在的通用大模型很强,但它们不懂具体的硬件参数。宋军试图让大模型用“100条数据”,就能在一小时内学会控制一个新的硬件。

这两位老兵,就像是生态里的“修路人”。当外界盯着模型怎么冲向SOTA(最前沿)时,他们默默低头,把落地的“最后一公里”修平了。

而在他们身后,还有一群默默“点火”的人。

创客火的严国陶就是其中的典型。作为深圳创客文化的见证者,他从最早混迹于柴火创客空间,到如今深耕无人机教育,干了整整12年。

他做的事,是把高冷的工业级无人机技术,拆解成了孩子们能上手*“空中乐高”。依托地瓜机器人的算力底座,他让学生们不再只是玩那个会飞的塑料玩具,而是能真正接触到边缘计算、视觉识别和集群编队这些硬核技术。

默默传递火种的,还有有方机器人的王明松有你同创的佘鹏飞,这几位教育领域的创业者,仿佛中国机器人沃土的“播种者”,确保未来十年,中国依然有源源不断的年轻人愿意跳进这个行业。

同在这个行业努力的,还有转战具身智能的连续创业者。

从上一家公司做健身AI“骗汗水”,到如今做陪伴机器人“骗感情”,Roy一直在思考一个问题:交互与人性的极致在哪里? 他试图给冷冰冰的机器人注入一种“狡黠”的温情,让具身智能不仅能干活,还能懂你。

这种对体验的极致追求,让他与传统的“工具型”开发者划清了界限。在他眼中,真正C端的产品不靠冷冰冰的参数堆砌,而靠细腻的情感共鸣——他要造的不是一台机器,而是一个能让用户产生依赖、甚至产生生命幻觉的新物种。

但最让人眼前一亮的,还是以朗极智能Peter为代表的“00后新势力”。

Peter和他的联合创始人们的故事,简直就是这一代年轻造物者的典型缩影。他们的起点纯粹是因为“好玩”——高中时为了酷,在学校里造了一辆能自平衡的自行车;大一时为了展示极客范儿,把自动驾驶算法塞进轮椅,造了一个纯视觉导航的智能轮椅。

那时候,这只是一个少年的天马行空,是单纯的“炫技”。

直到他们开始将简单的demo真正产品化,添加真正厚重的AI技术作为支撑,让它真正长成一个AI产品。

从智能轮椅长出了智能代步车,又长出了可以骑乘的户外mini皮卡——一辆长得像露营车,却有200公斤载重的小车;它没有方向盘,却能通过视觉像忠犬一样自动跟随主人,甚至提供主动助力。

从“智能轮椅”的Demo到“迷你皮卡”的商品,朗极智能解决的是露营圈的真实痛点。在Kickstarter上甚至还没发货,就众筹到了几十万美金。

 

 

从“玩票”到“量产”,为什么野性创意没有死在实验室?

 

Peter的故事很热血,但现实往往很冷酷。

像朗极智能这样从校园走出来的公司,在行业里其实是幸存者。对于绝大多数创业者来说,从学校实验室里那个让导师点头的“原型机”,到真正能够量产、能经受住市场毒打的“商品”,中间隔着一道深不见底的“死亡谷”。

对于一个学生团队,对于一个学生团队来说,算力成本、工程化难度、供应链资源……每一项都能让项目中途夭折。

为什么这些年轻的野性创意没有死在实验室里,而是真的长成了商业公司?

答案不在Peter一个人身上。或许我们能从这场机器人派对的主办方找到端倪——你会发现,所有登台的“幸存者”,脚下都踩着一块早已平整好的“土壤”——地瓜机器人的生态。

为什么Peter这样的00后,或者像韩冬这样跨界的“鱼塘娃”,能如此快速地拿出产品?

这与地瓜机器人近年来带来的技术上的“平权”有关。

得益于地瓜统一的软硬件地基。从RDK系列开发板到TogetheROS.Bot操作系统,地瓜把最难啃的底层硬骨头(芯片适配、感知算法、通信中间件)都替开发者啃完了。

这就好比是给开发者提供了一套通用的底盘。创业者不再需要从零去造轮子、去写最底层的驱动,他们只需要专注于自己最擅长的——去定义场景,去发挥那些天马行空的创意。地瓜机器人搭建的地基,正在让造机器人的门槛正在变得越来越低。

光有技术还不够,对于初创公司,尤其是像朗极智能这样的学生团队,最缺的往往不是代码,而是让公司活下去的“粮草”。

这就不得不提地瓜生态里的另一股核心力量——“地心引力计划”(DGP)。

在现场,我们看到了这个计划实打实的“含金量”。它不是虚头巴脑的口号,而是针对初创企业最痛的穴位精准施针:

  • 缺钱? 提供专属的产品折扣(20%以上),直接降低硬件BOM成本,让学生也造得起;
  • 缺人? 帮你连接500+院校的人才库,解决招人难;
  • 缺资源? 对接100+产业链伙伴和资本,甚至开放海外全经销渠道,帮你出海。

尤其对于像朗极智能这样从校园走出来的团队,最隐蔽的坑往往在“选型”上。

在实验室里做Demo,和真实世界的产品量产完全不同。一旦要变成量产的商品,每一块钱的BOM(物料)成本都决定了生与死。是用性能强悍的X5,还是性价比更高的X3?是用哪种传感器才能在户外稳定工作?这些问题,教科书里没写,却是量产路上的“鬼门关”。

这时候,“地心引力计划”提供的一对一技术支持和选型服务就显出了极高的含金量。手把手地帮你挑出那个最优解,是初创团队最稀缺、也最难在市场上买到的服务。

正是这种全方位的托举,让“地心引力”成为了对抗“创业阻力”的关键。它让孤独的徒步者找到了队友,让象牙塔里的学生找到了供应链,让技术老兵找到了落地场景。

走出那个热气腾腾的派对现场,笔者不禁在想:中国具身智能的未来,到底应该是什么样?

它不应该只有几只高耸入云、万众瞩目的“独角兽”,孤零零地立在荒原上。

它更需要像地瓜机器人这样肥沃的“土壤”,以及“地心引力”这样持续不断的能量供给。

只有底座稳了,生态才能像热带雨林一样:既有参天大树,也有像Kago这样好玩的灌木,更有像鱼塘机器人、外骨骼这样扎根泥土的野草。它们千奇百怪,它们野蛮生长,它们共同构成了一个生机勃勃的中国机器人江湖。

新莱福:新莱福资管计划拟减持公司不超87.43万股股份

36氪获悉,新莱福发布公告,持股87.43万股,占公司总股本的0.83%的新莱福资管计划拟通过大宗交易或集中竞价交易方式减持公司股份不超过87.43万股,不超过公司总股本的0.83%,不超过剔除公司当前回购专用账户股份后总股本的0.84%。

美联储理事沃勒称他主张在12月降息

美联储理事沃勒称他主张在12月降息。并表示,9月份就业数据可能会被下修,数据集中出现并非好兆头;明年一月份将充满挑战,大量数据将陆续公布,以判断是否需要再次降息,美联储需要逐次会议地进行评估。(财联社)

热门中概股美股盘前普涨,哔哩哔哩涨超6%

36氪获悉,热门中概股美股盘前普涨,截至发稿,哔哩哔哩涨超6%,百度、网易、蔚来涨超4%,小鹏汽车、阿里巴巴涨超3%,理想汽车、富途控股、爱奇艺、拼多多涨超1%。

kotlin-7

好的,我们将继续向 Kotlin 的更深层次探索。这部分将涵盖编译器内部原理、语言设计哲学、高级类型系统以及与其他语言的互操作等专家级主题。 Kotlin 专家级探索:语言设计与实现原理 四十六、Ko

kotlin-6

太棒了!现在让我们探索 Kotlin 的终极境界——元编程、编译器插件、深度优化和未来特性。这将带你进入 Kotlin 语言设计的核心领域。 Kotlin 终极探索:元编程与语言深度 三十九、注解处理

kotlin-5

太棒了!现在让我们进入 Kotlin 更深入的高级特性和实际应用场景。这部分将涵盖泛型、注解、反射、DSL等企业级开发必备技能。


Kotlin 高级特性:企业级开发实战

三十二、泛型深入:型变、星投影与 reified

1. 型变(Variance)
// 不变(Invariant)- 默认行为
class Box<T>(val value: T)

fun main() {
    val stringBox = Box("Hello")
    // val anyBox: Box<Any> = stringBox // 错误!Box<String> 不是 Box<Any> 的子类型
}

// 协变(Covariant)- 使用 out 关键字
class ReadOnlyBox<out T>(val value: T) {
    fun get(): T = value
    // fun set(newValue: T) {} // 错误!不能有消费 T 的方法
}

// 逆变(Contravariant)- 使用 in 关键字
class WriteOnlyBox<in T> {
    fun set(value: T) {
        println("设置值: $value")
    }
    // fun get(): T // 错误!不能有生产 T 的方法
}

fun testVariance() {
    // 协变示例
    val stringBox: ReadOnlyBox<String> = ReadOnlyBox("Text")
    val anyBox: ReadOnlyBox<Any> = stringBox // 正确!因为 out 关键字
    
    // 逆变示例
    val anyWriteBox: WriteOnlyBox<Any> = WriteOnlyBox()
    val stringWriteBox: WriteOnlyBox<String> = anyWriteBox // 正确!因为 in 关键字
}
2. 星投影(Star Projection)
fun printSize(list: List<*>) {
    println("列表大小: ${list.size}")
    // println(list[0]) // 不能安全地访问元素内容
}

fun <T> compareFirstSecond(list: List<T>, comparator: Comparator<in T>): Boolean 
    where T : Comparable<T> {
    return if (list.size >= 2) {
        comparator.compare(list[0], list[1]) > 0
    } else false
}

// 使用 reified 关键字实现类型检查
inline fun <reified T> checkType(value: Any): Boolean {
    return value is T
}

fun main() {
    val list = listOf(1, 2, 3)
    printSize(list)
    
    println(checkType<String>("Hello")) // true
    println(checkType<Int>("Hello"))    // false
}

三十三、注解与反射

1. 自定义注解
// 定义注解
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class ApiVersion(val version: String)

@Target(AnnotationTarget.PROPERTY)
annotation class JsonExclude

@Target(AnnotationTarget.PROPERTY)
annotation class JsonName(val name: String)

// 使用注解
@ApiVersion("1.0")
data class User(
    @JsonName("user_name")
    val name: String,
    
    val age: Int,
    
    @JsonExclude
    val password: String
)

// 注解处理器模拟
fun processAnnotations(obj: Any) {
    val klass = obj::class
    val apiVersion = klass.annotations.find { it is ApiVersion } as? ApiVersion
    apiVersion?.let { println("API 版本: ${it.version}") }
    
    klass.memberProperties.forEach { prop ->
        prop.annotations.forEach { annotation ->
            when (annotation) {
                is JsonName -> println("属性 ${prop.name} 将被序列化为 ${annotation.name}")
                is JsonExclude -> println("属性 ${prop.name} 将被排除")
            }
        }
    }
}
2. 反射实战
import kotlin.reflect.full.*
import kotlin.reflect.jvm.isAccessible

class SecretClass {
    private val secretValue = "机密信息"
    private fun secretMethod() = "机密方法"
    
    public fun publicMethod() = "公开方法"
}

fun reflectExample() {
    val instance = SecretClass()
    val klass = instance::class
    
    println("=== 类信息 ===")
    println("类名: ${klass.simpleName}")
    println("成员属性: ${klass.memberProperties.map { it.name }}")
    println("成员函数: ${klass.memberFunctions.map { it.name }}")
    
    println("\n=== 访问私有成员 ===")
    // 访问私有属性
    val secretProperty = klass.memberProperties.find { it.name == "secretValue" }
    secretProperty?.let {
        it.isAccessible = true
        println("私有属性值: ${it.get(instance)}")
    }
    
    // 调用私有方法
    val secretMethod = klass.memberFunctions.find { it.name == "secretMethod" }
    secretMethod?.let {
        it.isAccessible = true
        println("私有方法结果: ${it.call(instance)}")
    }
}

三十四、类型安全的构建器(DSL)

1. HTML DSL 构建器
class HtmlElement(val name: String, val content: String = "") {
    private val children = mutableListOf<HtmlElement>()
    private val attributes = mutableMapOf<String, String>()
    
    fun attribute(name: String, value: String) {
        attributes[name] = value
    }
    
    fun child(element: HtmlElement) {
        children.add(element)
    }
    
    override fun toString(): String {
        val attrString = if (attributes.isNotEmpty()) {
            " " + attributes.entries.joinToString(" ") { "${it.key}="${it.value}"" }
        } else ""
        
        return if (children.isEmpty()) {
            "<$name$attrString>$content</$name>"
        } else {
            "<$name$attrString>${children.joinToString("")}$content</$name>"
        }
    }
}

// DSL 构建函数
fun html(block: HtmlBuilder.() -> Unit): HtmlElement {
    val builder = HtmlBuilder("html")
    builder.block()
    return builder.build()
}

class HtmlBuilder(private val rootName: String) {
    private val root = HtmlElement(rootName)
    private var currentElement: HtmlElement = root
    
    fun head(block: HeadBuilder.() -> Unit) {
        val headBuilder = HeadBuilder()
        headBuilder.block()
        root.child(headBuilder.build())
    }
    
    fun body(block: BodyBuilder.() -> Unit) {
        val bodyBuilder = BodyBuilder()
        bodyBuilder.block()
        root.child(bodyBuilder.build())
    }
    
    fun build(): HtmlElement = root
}

class HeadBuilder {
    private val head = HtmlElement("head")
    
    fun title(text: String) {
        head.child(HtmlElement("title", text))
    }
    
    fun build(): HtmlElement = head
}

class BodyBuilder {
    private val body = HtmlElement("body")
    
    fun h1(text: String, block: H1Builder.() -> Unit = {}) {
        val h1Builder = H1Builder(text)
        h1Builder.block()
        body.child(h1Builder.build())
    }
    
    fun p(text: String) {
        body.child(HtmlElement("p", text))
    }
    
    fun build(): HtmlElement = body
}

class H1Builder(private val text: String) {
    private val h1 = HtmlElement("h1", text)
    
    fun style(css: String) {
        h1.attribute("style", css)
    }
    
    fun build(): HtmlElement = h1
}

// 使用 DSL
fun createHtmlPage() {
    val page = html {
        head {
            title("我的网页")
        }
        body {
            h1("欢迎来到 Kotlin DSL") {
                style("color: blue;")
            }
            p("这是一个使用 DSL 构建的 HTML 页面")
        }
    }
    
    println(page)
}
2. 数据库查询 DSL
// 查询 DSL 定义
class QueryBuilder {
    private var table: String = ""
    private val conditions = mutableListOf<String>()
    private var limit: Int? = null
    private var orderBy: String? = null
    
    fun from(table: String) {
        this.table = table
    }
    
    fun where(condition: String) {
        conditions.add(condition)
    }
    
    fun limit(count: Int) {
        this.limit = count
    }
    
    fun orderBy(column: String) {
        this.orderBy = column
    }
    
    fun build(): String {
        val query = StringBuilder("SELECT * FROM $table")
        
        if (conditions.isNotEmpty()) {
            query.append(" WHERE ").append(conditions.joinToString(" AND "))
        }
        
        orderBy?.let {
            query.append(" ORDER BY $it")
        }
        
        limit?.let {
            query.append(" LIMIT $it")
        }
        
        return query.toString()
    }
}

// DSL 入口函数
fun query(block: QueryBuilder.() -> Unit): String {
    val builder = QueryBuilder()
    builder.block()
    return builder.build()
}

// 使用 DSL 构建查询
fun buildQueries() {
    val simpleQuery = query {
        from("users")
    }
    println("简单查询: $simpleQuery")
    
    val complexQuery = query {
        from("orders")
        where("status = 'completed'")
        where("amount > 100")
        orderBy("created_at DESC")
        limit(10)
    }
    println("复杂查询: $complexQuery")
}

三十五、合约(Contracts)与内联优化

import kotlin.contracts.*

// 使用合约优化智能转换
@OptIn(ExperimentalContracts::class)
fun String?.isNotNullOrEmpty(): Boolean {
    contract {
        returns(true) implies (this@isNotNullOrEmpty != null)
    }
    return this != null && this.isNotEmpty()
}

fun processText(text: String?) {
    if (text.isNotNullOrEmpty()) {
        // 由于合约,编译器知道 text 不为 null
        println(text.length) // 不需要安全调用
        println(text.uppercase())
    }
}

// 内联类 - 包装类型而不产生运行时开销
@JvmInline
value class Password(private val value: String) {
    init {
        require(value.length >= 8) { "密码必须至少8个字符" }
    }
    
    fun mask(): String = "*".repeat(value.length)
}

@JvmInline
value class UserId(val value: Int)

fun login(userId: UserId, password: Password) {
    println("用户 ${userId.value} 登录,密码: ${password.mask()}")
}

三十六、多平台项目(KMP)基础

// 公共模块 - 共享业务逻辑
expect class Platform() {
    val platform: String
}

class Greeting {
    private val platform = Platform()
    
    fun greet(): String {
        return "Hello from ${platform.platform}"
    }
}

// Android 实现
// androidMain/Platform.kt
actual class Platform actual constructor() {
    actual val platform: String = "Android"
}

// iOS 实现  
// iosMain/Platform.kt
actual class Platform actual constructor() {
    actual val platform: String = "iOS"
}

// 共享业务逻辑
class Calculator {
    fun add(a: Int, b: Int): Int = a + b
    fun multiply(a: Int, b: Int): Int = a * b
    
    fun calculateExpression(expression: String): Int {
        return when {
            expression.contains("+") -> {
                val parts = expression.split("+")
                add(parts[0].trim().toInt(), parts[1].trim().toInt())
            }
            expression.contains("*") -> {
                val parts = expression.split("*")
                multiply(parts[0].trim().toInt(), parts[1].trim().toInt())
            }
            else -> throw IllegalArgumentException("不支持的表达式")
        }
    }
}

三十七、性能优化与最佳实践

1. 集合操作优化
fun collectionOptimization() {
    val numbers = (1..1000000).toList()
    
    // 不好的写法:多次中间操作
    val badResult = numbers
        .filter { it % 2 == 0 }
        .map { it * 2 }
        .filter { it > 1000 }
        .take(10)
    
    // 好的写法:使用序列(惰性求值)
    val goodResult = numbers.asSequence()
        .filter { it % 2 == 0 }
        .map { it * 2 }
        .filter { it > 1000 }
        .take(10)
        .toList()
    
    println("结果: $goodResult")
}

// 使用 groupBy 优化复杂操作
data class Person(val name: String, val age: Int, val city: String)

fun optimizeGrouping() {
    val people = listOf(
        Person("Alice", 25, "Beijing"),
        Person("Bob", 30, "Shanghai"),
        Person("Charlie", 25, "Beijing"),
        Person("Diana", 30, "Shanghai")
    )
    
    // 按城市分组,然后按年龄分组
    val grouped = people.groupBy({ it.city }, { it.age })
    println("分组结果: $grouped")
    
    // 统计每个城市的平均年龄
    val averageAges = people.groupingBy { it.city }
        .fold(0.0) { accumulator, element -> 
            accumulator + element.age 
        }.mapValues { it.value / people.count { p -> p.city == it.key } }
    
    println("平均年龄: $averageAges")
}
2. 内存优化模式
// 使用对象池避免重复创建
class ConnectionPool private constructor() {
    private val available = mutableListOf<Connection>()
    private val inUse = mutableSetOf<Connection>()
    
    fun getConnection(): Connection {
        return available.removeFirstOrNull()?.also { inUse.add(it) } 
            ?: Connection().also { inUse.add(it) }
    }
    
    fun releaseConnection(connection: Connection) {
        inUse.remove(connection)
        available.add(connection)
    }
    
    companion object {
        val instance by lazy { ConnectionPool() }
    }
}

class Connection {
    fun connect() = println("连接建立")
    fun disconnect() = println("连接关闭")
}

// 使用 use 函数自动管理资源
fun readFileSafely() {
    // 模拟资源管理
    val resource = object : AutoCloseable {
        override fun close() = println("资源已释放")
        fun read() = "文件内容"
    }
    
    val content = resource.use { 
        it.read() 
    }
    println("内容: $content") // 资源会自动关闭
}

三十八、完整实战项目:构建事件总线系统

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.flow.*
import kotlin.reflect.KClass

// 事件基类
sealed class Event {
    data class UserLoggedIn(val userId: String) : Event()
    data class OrderCreated(val orderId: String, val amount: Double) : Event()
    data class PaymentProcessed(val paymentId: String, val success: Boolean) : Event()
    object SystemShutdown : Event()
}

// 事件处理器接口
interface EventHandler<T : Event> {
    suspend fun handle(event: T)
}

// 事件总线
object EventBus {
    private val handlers = mutableMapOf<KClass<out Event>, MutableList<EventHandler<*>>>()
    private val eventChannel = Channel<Event>(Channel.UNLIMITED)
    
    // 注册事件处理器
    fun <T : Event> registerHandler(eventClass: KClass<T>, handler: EventHandler<T>) {
        handlers.getOrPut(eventClass) { mutableListOf() }.add(handler as EventHandler<*>)
    }
    
    // 发布事件
    fun publish(event: Event) {
        eventChannel.trySend(event)
    }
    
    // 启动事件处理循环
    fun start() = GlobalScope.launch {
        for (event in eventChannel) {
            processEvent(event)
        }
    }
    
    private suspend fun processEvent(event: Event) {
        val eventClass = event::class
        val eventHandlers = handlers[eventClass] ?: return
        
        eventHandlers.forEach { handler ->
            try {
                @Suppress("UNCHECKED_CAST")
                (handler as EventHandler<Event>).handle(event)
            } catch (e: Exception) {
                println("事件处理失败: ${e.message}")
            }
        }
    }
}

// 具体的事件处理器
class UserActivityLogger : EventHandler<Event.UserLoggedIn> {
    override suspend fun handle(event: Event.UserLoggedIn) {
        delay(100) // 模拟处理时间
        println("📝 用户登录日志: ${event.userId}${System.currentTimeMillis()} 登录")
    }
}

class OrderProcessor : EventHandler<Event.OrderCreated> {
    override suspend fun handle(event: Event.OrderCreated) {
        delay(200)
        println("💰 订单处理: 订单 ${event.orderId} 金额 ${event.amount}")
    }
}

class PaymentNotifier : EventHandler<Event.PaymentProcessed> {
    override suspend fun handle(event: Event.PaymentProcessed) {
        delay(150)
        val status = if (event.success) "成功" else "失败"
        println("📧 支付通知: 支付 ${event.paymentId} $status")
    }
}

class SystemMonitor : EventHandler<Event.SystemShutdown> {
    override suspend fun handle(event: Event.SystemShutdown) {
        println("🛑 系统关闭: 开始清理资源...")
        delay(500)
        println("🛑 系统关闭: 资源清理完成")
    }
}

// 使用事件总线
fun main() = runBlocking {
    // 注册处理器
    EventBus.registerHandler(Event.UserLoggedIn::class, UserActivityLogger())
    EventBus.registerHandler(Event.OrderCreated::class, OrderProcessor())
    EventBus.registerHandler(Event.PaymentProcessed::class, PaymentNotifier())
    EventBus.registerHandler(Event.SystemShutdown::class, SystemMonitor())
    
    // 启动事件总线
    EventBus.start()
    
    // 模拟发布事件
    EventBus.publish(Event.UserLoggedIn("user123"))
    EventBus.publish(Event.OrderCreated("order456", 99.99))
    EventBus.publish(Event.PaymentProcessed("pay789", true))
    EventBus.publish(Event.OrderCreated("order999", 49.99))
    EventBus.publish(Event.PaymentProcessed("pay000", false))
    
    delay(1000) // 等待事件处理完成
    
    EventBus.publish(Event.SystemShutdown())
    delay(1000)
    
    println("程序结束")
}

下一步学习方向

你现在已经掌握了 Kotlin 的企业级开发技能!接下来可以深入:

  1. Kotlin 编译器插件开发:自定义编译期处理
  2. Kotlin 元编程:在编译时生成代码
  3. Kotlin 与 Java 互操作高级技巧:类型映射、异常处理
  4. Kotlin 服务端开发:使用 Ktor、Spring Boot
  5. Kotlin 前端开发:使用 Compose for Web
  6. Kotlin 移动端开发:Compose Multiplatform

这些高级特性将让你在 Kotlin 开发中游刃有余,能够构建复杂、高性能的企业级应用!

🗣️面试官: 那些常见的前端面试场景问题

1. 页面白屏如何排查?

第一步:快速分类(30秒) "页面白屏主要有五种原因:JavaScript执行错误、资源加载失败、CSS样式问题、接口异常和浏览器兼容性。其中JavaScript错误最常见,特别是SPA应用中的未捕获异常。"

第二步:排查方法(1分钟) "我的排查步骤是:首先查看Console面板的错误信息,这能快速定位JS异常;然后检查Network面板确认资源加载状态;接着用Elements面板验证DOM和样式;移动端问题会用vConsole或真机调试。生产环境结合Sentry等监控系统分析。"

第三步:预防措施(30秒) "预防方面建立错误边界、资源容错机制、统一接口异常处理、兼容性检测,同时搭建监控告警体系。"

一、基础检测流程

1. 控制台检查(Console)

// 主动捕获全局错误(放在入口文件最前面)
window.addEventListener('error', function(event) {
  console.error('全局捕获:', event.error);
  // 可上报到监控系统
});

// 检查console是否有以下类型错误:
// - SyntaxError (语法错误)
// - TypeError (类型错误)
// - ReferenceError (引用错误)
// - 404资源加载失败

2. 网络请求检查(Network)

  • 关键指标

    • HTML文档状态码(200/304/404/500)
    • JS/CSS资源加载状态
    • 接口请求是否阻塞渲染
  • 检测示例

// 检查关键资源是否加载完成
const resourceCheck = () => {
  const entries = performance.getEntriesByType('resource');
  const criticalResources = entries.filter(entry => 
    entry.initiatorType === 'script' || 
    entry.initiatorType === 'css'
  );
  
  criticalResources.forEach(res => {
    if(res.responseStatus >= 400) {
      console.error(`资源加载失败: ${res.name}`, res);
    }
  });
};
window.addEventListener('load', resourceCheck);

二、深度检测方法

1. DOM渲染检测

// 检测DOM树是否正常构建
function checkDOMReady() {
  return new Promise((resolve) => {
    const check = () => {
      if(document.body && document.body.children.length > 0) {
        resolve(true);
      } else {
        setTimeout(check, 50);
      }
    };
    check();
  });
}

// 使用示例
checkDOMReady().then((isReady) => {
  if(!isReady) {
    console.error('DOM渲染超时');
    // 上报白屏信息
  }
});

2. 框架特定检测

Vue应用检测:
// 在main.js中添加
new Vue({
  render: h => h(App),
  errorCaptured(err, vm, info) {
    console.error('Vue组件错误:', err, info);
    // 可上报错误
    return false; // 阻止错误继续向上传播
  }
}).$mount('#app');

// 检查根组件挂载
if(!document.querySelector('#app').__vue__) {
  console.error('Vue根实例挂载失败');
}
React应用检测:
// Error Boundary组件
class ErrorBoundary extends React.Component {
  componentDidCatch(error, info) {
    console.error('React组件错误:', error, info);
    // 上报错误
  }
  
  render() {
    return this.props.children; 
  }
}

// 检查React根组件
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <ErrorBoundary>
    <App />
  </ErrorBoundary>
);

三、性能相关检测

1. 长任务检测

// 检测阻塞渲染的长时间任务
const observer = new PerformanceObserver((list) => {
  for(const entry of list.getEntries()) {
    if(entry.duration > 50) { // 超过50ms的任务
      console.warn('长任务影响渲染:', entry);
    }
  }
});
observer.observe({entryTypes: ["longtask"]});

2. 关键渲染路径监控

// 使用Performance API监控关键时间点
const perfData = window.performance.timing;
const metrics = {
  domReady: perfData.domComplete - perfData.domLoading,
  loadTime: perfData.loadEventEnd - perfData.navigationStart
};

if(metrics.domReady > 3000) {
  console.error('DOM解析时间过长:', metrics);
}

四、自动化检测方案

1. Puppeteer检测脚本

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  // 监听控制台错误
  page.on('console', msg => {
    if(msg.type() === 'error') {
      console.log('页面错误:', msg.text());
    }
  });
  
  // 设置超时检测
  await Promise.race([
    page.goto('https://your-site.com'),
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error('页面加载超时')), 5000)
    )
  ]);
  
  // 检查可见内容
  const content = await page.evaluate(() => {
    return {
      bodyText: document.body.innerText,
      childCount: document.body.children.length
    };
  });
  
  if(content.childCount === 0 || content.bodyText.length < 10) {
    console.error('检测到白屏现象');
  }
  
  await browser.close();
})();

2. 真实用户监控(RUM)

// 使用浏览器的MutationObserver监控DOM变化
const observer = new MutationObserver((mutations) => {
  if(!document.querySelector('#app')?.innerHTML) {
    // 上报白屏事件
    beacon.send('white-screen', {
      url: location.href,
      ua: navigator.userAgent
    });
  }
});

observer.observe(document.body, {
  childList: true,
  subtree: true
});

五、常见白屏场景示例

  1. 资源加载失败

    <!-- 错误的资源路径 -->
    <script src="/wrong-path/app.js"></script>
    
  2. 语法错误

    // 缺少括号导致整个脚本不执行
    function test() {
      console.log('hello'
    }
    
  3. 框架初始化失败

    // Vue示例 - 挂载元素不存在
    new Vue({el: '#not-exist'});
    
  4. CSS阻塞

    <!-- 不正确的CSS引入阻塞渲染 -->
    <link rel="stylesheet" href="nonexist.css">
    
  5. 第三方库冲突

    // 两个库都修改了Array原型
    libraryA.modifyPrototype();
    libraryB.modifyPrototype(); // 冲突导致错误 ```
    

2.前端埋点

一、页面生命周期埋点

1. 页面加载阶段

// 记录页面开始加载时间
const pageStartTime = Date.now();

// 监听页面加载完成
window.addEventListener('load', () => {
  const loadTime = Date.now() - pageStartTime;
  track('page_load', {
    load_time: loadTime,
    referrer: document.referrer,
    resource_status: checkResources()
  });
});

// 检查关键资源加载状态
function checkResources() {
  return performance.getEntriesByType('resource').map(res => ({
    name: res.name,
    type: res.initiatorType,
    duration: res.duration.toFixed(2)
  }));
}

2. 用户交互阶段

// 点击事件埋点(支持事件委托)
document.body.addEventListener('click', (e) => {
  const target = e.target.closest('[data-track]');
  if(target) {
    track('element_click', {
      element_id: target.id,
      track_type: target.dataset.track,
      position: `${e.clientX},${e.clientY}`
    });
  }
});

// 滚动深度记录
let maxScroll = 0;
window.addEventListener('scroll', _.throttle(() => {
  const currentScroll = window.scrollY / document.body.scrollHeight;
  if(currentScroll > maxScroll) {
    maxScroll = currentScroll;
    track('scroll_depth', { depth: Math.round(maxScroll * 100) });
  }
}, 1000));

3. 页面停留时长计算

let activeStart = Date.now();
let inactiveTime = 0;

// 用户活跃状态检测
document.addEventListener('mousemove', resetActiveTimer);
document.addEventListener('keydown', resetActiveTimer);

function resetActiveTimer() {
  if(inactiveTime > 0) {
    track('user_inactive', { duration: inactiveTime });
    inactiveTime = 0;
  }
  activeStart = Date.now();
}

// 每10秒检测一次活跃状态
setInterval(() => {
  if(Date.now() - activeStart > 15000) { // 15秒无操作视为不活跃
    inactiveTime += 10000;
  } else {
    track('user_active', { duration: 10000 });
  }
}, 10000);

二、特殊场景处理

1. 页面隐藏/显示

// 页面可见性变化监听
document.addEventListener('visibilitychange', () => {
  if(document.hidden) {
    track('page_hide', { 
      stay_time: Date.now() - pageStartTime,
      scroll_depth: maxScroll
    });
  } else {
    track('page_show');
  }
});

2. 页面关闭前上报

// 确保页面关闭前数据上报
window.addEventListener('beforeunload', () => {
  const totalStay = Date.now() - pageStartTime;
  navigator.sendBeacon('/api/track', JSON.stringify({
    event: 'page_close',
    active_time: totalStay - inactiveTime,
    scroll_depth: maxScroll
  }));
});

三、数据上报优化方案

1. 批量上报机制

let eventQueue = [];
const MAX_QUEUE = 5;
const FLUSH_INTERVAL = 3000;

function addToQueue(event) {
  eventQueue.push(event);
  if(eventQueue.length >= MAX_QUEUE) {
    flushQueue();
  }
}

function flushQueue() {
  if(eventQueue.length === 0) return;
  
  const batchData = { batch: eventQueue };
  navigator.sendBeacon('/api/batch', JSON.stringify(batchData));
  eventQueue = [];
}

// 定时刷新队列
setInterval(flushQueue, FLUSH_INTERVAL);

2. 关键指标计算

// 计算FMP(首次有效绘制)
new PerformanceObserver((entryList) => {
  const [entry] = entryList.getEntriesByName('first-contentful-paint');
  track('fmp', { value: entry.startTime.toFixed(2) });
}).observe({type: 'paint', buffered: true});

// 计算LCP(最大内容绘制)
new PerformanceObserver((entryList) => {
  const entries = entryList.getEntries();
  const lastEntry = entries[entries.length - 1];
  track('lcp', { value: lastEntry.startTime.toFixed(2) });
}).observe({type: 'largest-contentful-paint', buffered: true});

四、面试回答精简版

"我们实现全链路埋点主要分三个阶段:

  1. 加载阶段

    • performance API采集DNS/TTFB等指标
    • 监听load事件记录完整加载时间
    • 检查关键资源状态(如图片/脚本)
  2. 交互阶段

    • 事件委托监听全局点击(带data-track属性)
    • 节流处理滚动事件计算最大深度
    • 通过mousemove/keydown检测活跃状态
  3. 离开阶段

    • visibilitychange处理页面切换
    • beforeunload+sendBeacon确保关闭前上报
    • 计算总停留时长和有效活跃时间

3.那为什么大家都使用请求 GIF 图片的方式上报埋点数据呢?

  • 防止跨域问题:前端监控的请求常常会遇到跨域问题,这可能会影响监控的准确性和可用性。然而,图片的src属性并不会跨域,因此使用GIF图片作为埋点可以正常发起请求,从而有效避免跨域问题。

  • 防止阻塞页面加载:在创建资源节点后,通常只有当对象注入到浏览器的DOM树后,浏览器才会实际发送资源请求。但反复操作DOM会引发性能问题,且载入js/css资源会阻塞页面渲染,从而影响用户体验。与此不同,构造图片打点不需要插入DOM,只要在js中new出Image对象就能发起请求,这样就不会有阻塞问题。即使在没有js的浏览器环境中,也能通过img标签正常打点,这是其他类型的资源请求所做不到的。

  • 体积小,节约流量:相比其他图片格式(如BMP和PNG),GIF图片具有更小的体积。例如,最小的BMP文件需要74个字节,PNG需要67个字节,而合法的GIF只需要43个字节。因此,使用GIF作为埋点可以显著节约流量,提高数据传输效率。

  • 浏览器支持性好:所有浏览器都支持Image对象,即使不支持XMLHttpRequest对象也一样。这意味着使用GIF进行埋点上报可以在各种浏览器环境中稳定运行。

  • 记录错误的过程很少出错:某些情况下,如Ajax通信过程中页面跳转,请求可能会被取消。但使用图片进行埋点上报则不会遇到这个问题,特别是在记录离开页面打点行为的时候会很有用。

120行代码,实现丝滑滚动的时间轴组件

前言

产品又看见一个非常高级的时间轴页面交互(对于前端来说,实现不难,主要是花时间),我们尝试一下让Trae 的solo来实现,最简洁的代码实现,尽可能的描述一下细节,这样才可以最准确的实现,少走弯路,不浪费我们宝贵的时间(有这时间多看几篇技术文章,不香?啊哈哈哈)

首先先搭建好一个项目,这里使用的是vite+vue3+ts,然后是对应的安装tailwind css,这些都是让Trae solo模式帮我们搭建一下,我们就不用从0搭建了

image-20251120152839739

先来看看最终的效果实现,你很难相信这是一次对话就实现的

image.png 一.首先是描述细节

1.时间轴的中间是可以滚动的,然后要根据鼠标进行滚动,不要滚动条

2.奇数是在上面,时间轴的连接线比较长,偶数是在下面,连接线比较短

3.箭头在右下角固定,保证用户知道这个是时间轴

4.卡片最大宽度是240px,超出隐藏

5.线和卡片中间使用倒三角连接起来

附加上一张ui设计的截图,就可以开始提问了

image.png

等待了一会,就实现了

来看看代码,119行就实现了,看起来确实优雅,解读一下代码trae solo是如何实现这个组件的

1.卡片(奇偶项交错,下移以视觉区分),使用tailwind的mt-[210px],

<div
          class="relative max-w-[230px] overflow-hidden rounded-lg bg-[#1D2129] shadow-lg"
          :class="i % 2 !== 0 ? 'mt-[210px]' : ''"
        >
          <!-- 卡片头部:深色背景 -->
          <div class="flex items-center justify-between px-3 py-2">
            <span class="text-sm font-medium text-white">共7条</span>
            <span class="cursor-pointer text-xs text-white">全部></span>
          </div>

          <!-- 卡片内容:两张图片并排 -->
          <div class="flex gap-2 overflow-hidden p-3">
            <div class="relative shrink-0 overflow-hidden rounded">
              <img :src="item.image" class="aspect-video h-[150px] w-[89px] object-cover" alt="">
            </div>
            <div class="relative shrink-0 overflow-hidden rounded">
              <img :src="item.image" class="aspect-video h-[150px] w-[89px] object-cover" alt="">
            </div>
            <div class="relative shrink-0 overflow-hidden rounded">
              <img :src="item.image" class="aspect-video h-[150px] w-[89px] object-cover" alt="">
            </div>
          </div>
        </div>

2.底部小三角形指示器,有没有勾起你当时学前端的回忆,那道经典的面试题的,你是怎么实现一个三角形的

<div class="border-x-[12px] border-t-[12px] border-[#1D2129] border-x-transparent" />

3.竖线:奇数项加长(视觉第1、3、5项),使用tailwind的h-[310px]和h-[101px]来实现连接线的长短

<div
          class="w-[2px]" :class="[            isCreation ? 'bg-blue-500' : 'bg-[#685FE1]',            i % 2 === 0 ? 'h-[310px]' : 'h-[101px]',          ]"
        />

4.底部圆点的实现,也是使用一个div来实现的

<div
    class="z-10 size-4 rounded-full border-[3px] bg-white" :class="[    isCreation ? 'border-blue-500' : 'border-[#685FE1]',    ]"
/>

5.底部横向主线(黑色)贯穿整个容器

<div class="absolute inset-x-0 bottom-[35px] h-[2px] bg-black"/>

6.底部时间轴右侧箭头(固定在轴线右端,靠内边距 right-[-11px] 可调)

  <div class="pointer-events-none absolute bottom-[56px] right-[-11px] z-[999]">
    <!-- 向右的箭头 SVG,样式可改 -->
    <svg class="size-6 text-black" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
      <path d="M5 12h13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
      <path d="M13 5l7 7-7 7" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
    </svg>
  </div>

关键代码,通过监听鼠标滚轮来实现横向滚动的

onMounted(() => {
  const el = scrollRef.value
  if (!el)
    return
  // 鼠标滚轮横向滚动
  el.addEventListener('wheel', (e) => {
    if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
      e.preventDefault()
      el.scrollLeft += e.deltaY
    }
  })
})

来看看代码行数,确实很惊艳,简单的119行就实现了 image.png

总结

trae的solo确实比之前好很多,也惊艳到我了,前端可真是越来越难了,以后的时间排期也会进一步的压缩了,还是要适当的使用ai来辅助我们的编程,把一些比较容易实现的,耗时间的工作交给ai去实现,我们专注于一些工程化和架构上面去,也可以适当学一些node项目,提高我们自己的核心竞争力,加油吧,前端工程师们。

简单题,简单做(Python/Java/C++/C/Go/JS/Rust)

题意:计算 $\textit{nums}$ 每个前缀的二进制数值 $x$,判断 $x$ 是否为 $5$ 的倍数。

比如 $\textit{nums}=[1,1,0,1]$,每个前缀对应的二进制数分别为 $1,11,110,1101$。

如何计算这些二进制数呢?

在十进制中,我们往 $12$ 的右边添加 $3$,得到 $123$,做法是 $12\cdot 10 + 3 = 123$。

对于二进制,做法类似,往 $110$ 的右边添加 $1$,得到 $1101$,做法是 $110\cdot 2 + 1 = 1101$,或者 $110\ \texttt{<<}\ 1\ |\ 1 = 1101$。

注意本题 $\textit{nums}$ 很长,算出的二进制数 $x$ 很大,但我们只需要判断 $x\bmod 5=0$ 是否成立。可以在中途取模,也就是每次循环计算出新的 $x$ 后,把 $x$ 替换成 $x\bmod 5$。为什么可以在中途取模?原理见 模运算的世界:当加减乘除遇上取模

###py

class Solution:
    def prefixesDivBy5(self, nums: List[int]) -> List[bool]:
        ans = [False] * len(nums)
        x = 0
        for i, bit in enumerate(nums):
            x = (x << 1 | bit) % 5
            ans[i] = x == 0
        return ans

###java

class Solution {
    public List<Boolean> prefixesDivBy5(int[] nums) {
        List<Boolean> ans = new ArrayList<>(nums.length); // 预分配空间
        int x = 0;
        for (int bit : nums) {
            x = (x << 1 | bit) % 5;
            ans.add(x == 0);
        }
        return ans;
    }
}

###cpp

class Solution {
public:
    vector<bool> prefixesDivBy5(vector<int>& nums) {
        vector<bool> ans(nums.size());
        int x = 0;
        for (int i = 0; i < nums.size(); i++) {
            x = (x << 1 | nums[i]) % 5;
            ans[i] = x == 0;
        }
        return ans;
    }
};

###c

bool* prefixesDivBy5(int* nums, int numsSize, int* returnSize) {
    *returnSize = numsSize;
    bool* ans = malloc(numsSize * sizeof(bool));
    int x = 0;
    for (int i = 0; i < numsSize; i++) {
        x = (x << 1 | nums[i]) % 5;
        ans[i] = x == 0;
    }
    return ans;
}

###go

func prefixesDivBy5(nums []int) []bool {
ans := make([]bool, len(nums))
x := 0
for i, bit := range nums {
x = (x<<1 | bit) % 5
ans[i] = x == 0
}
return ans
}

###js

var prefixesDivBy5 = function(nums) {
    const ans = new Array(nums.length);
    let x = 0;
    for (let i = 0; i < nums.length; i++) {
        x = ((x << 1) | nums[i]) % 5;
        ans[i] = x === 0;
    }
    return ans;
};

###rust

impl Solution {
    pub fn prefixes_div_by5(nums: Vec<i32>) -> Vec<bool> {
        let mut ans = vec![false; nums.len()];
        let mut x = 0;
        for (i, bit) in nums.into_iter().enumerate() {
            x = (x << 1 | bit) % 5;
            ans[i] = x == 0;
        }
        ans
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{nums}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。返回值不计入。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

❌