在Rust代码中编写Python是种怎样的体验?
作者 | Mara Bos,Rust资深工程师
译者 | Arvin,编辑 | 屠敏
来源 | CSDN(ID:CSDNnews)
大约一年前,我发布了一个名为inline-python(https://crates.io/crates/inline-python)的Rust类库,它允许大家使用python!{ .. }宏轻松地将一些Python混合到Rust代码中。在本系列中,我将从头展示开发此类库的过程。
预览
如果不熟悉inline-python类库,你可以执行以下操作:
fn main() {let who = "world";let n = 5;python! {for i in range('n):print(i, "Hello", 'who)print("Goodbye")}
}
它允许你将Python代码直接嵌入Rust代码行之间,甚至直接在Python代码中使用Rust变量。
我们将从一个比这个简单得多的案例开始,然后逐步努力以达到这个结果(甚至更多!)。
运行Python代码
首先,让我们看一下如何在Rust中运行Python代码。让我们尝试使第一个简单的示例生效:
fn main() {println!("Hello ...");run_python("print(\"... World!\")");
}
我们可以使用std::process::命令来运行python可执行文件并传递python代码,从而实现run_python,但如果我们希望能够定义和读回Python变量,那么最好从使用PyO3库开始。
PyO3为我们提供了Python的Rust绑定。它很好地包装了Python C API,使我们可以直接在Rust中与各种Python对象交互。(甚至在Rust中编写Python库,但这是另一个主题。)
它的Python::run 功能完全符合我们的需求。它将Python代码作为&str,并允许我们使用两个可选的PyDicts 来定义范围内的任何变量。让我们试一试吧:
fn run_python(code: &str) {let py = pyo3::Python::acquire_gil(); // Acquire the 'global interpreter lock', as Python is not thread-safe.py.python().run(code, None, None).unwrap(); // No locals, no globals.
}
$ cargo runCompiling scratchpad v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.29sRunning `target/debug/scratchpad`
Hello ...
... World!
看,这就成功了!
基于规则的宏
在字符串中编写Python不是最便捷的方法,所以我们尝试改进它。宏允许我们在Rust中自定义语法,所以让我们尝试一下:
fn main() {println!("Hello ...");python! {print("... World!")}
}
宏通常是使用macro_rules!进行定义,您可以基于标记和表达式之类的内容使用高级“查找和替换”规则来定义宏。(有关macro_rules!的介绍请参见Rust Book中有关宏的章节,有关Rust宏所有的细节都可以在《Rust宏的小书》中找到。)
由macro_rules!定义的宏在编译时无法执行任何代码,这些宏仅是应用了基于模式的替换规则。它非常适合vec![],甚至是lazy_static!{ .. },但对于解析和编译正则表达式(例如regex!("a.*b"))之类的功能而言,还不够强大。
在宏的匹配规则中,我们可以匹配表达式,标识符,类型和许多其他内容。由于“有效的Python代码”不是一个选项,所以我们只能让宏接受所有内容:大量的原始的符号:
macro_rules! python {($($code:tt)*) => {...}
}
(有关macro_rules!工作原理的详细信息,请参见上面链接的资源。)
对宏的调用应该产生run_python(".."),这是一个包裹了所有Python代码的字符串文本。幸运的是:有一个内建宏为我们把内容放到一个字符串里,叫做stringify!,因此我不必从头开始。
macro_rules! python {($($code:tt)*) => {run_python(stringify!($($code)*));}
}
结果如下:
$ cargo rCompiling scratchpad v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.32sRunning `target/debug/scratchpad`
Hello ...
... World!
如愿以偿得到了期望结果!
但是,如果我们有不止一行的Python代码会怎样?
fn main() {println!("Hello ...");python! {print("... World!")print("Bye.")}
}
$ cargo rCompiling scratchpad v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.31sRunning `target/debug/scratchpad`
Hello ...
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: PyErr { type: Py(0x7f1c0a5649a0, PhantomData) }', src/main.rs:9:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
很不幸,我们失败了。
为了进行调试,我们需要正确输出PyErr,并显示我们传递给Python::run的确切Python代码:
fn run_python(code: &str) {println!("-----");println!("{}", code);println!("-----");let py = pyo3::Python::acquire_gil();if let Err(e) = py.python().run(code, None, None) {e.print(py.python());}
}
$ cargo rCompiling scratchpad v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.27sRunning `target/debug/scratchpad`
Hello ...
-----
print("... World!") print("Bye.")
-----File "<string>", line 1print("... World!") print("Bye.")^
SyntaxError: invalid syntax
很显然,两行Python代码落在同一行,在Python中这是无效的语法。
现在我们遇到了必须克服的最大问题:stringify!把空白符搞乱了.
空白符和符号
让我们仔细研究一下stringify!:
fn main() {println!("{}", stringify!(a 123 b cx ( y + z )// comment...));
}
$ cargo rCompiling scratchpad v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.21sRunning `target/debug/scratchpad`
a 123 b c x(y + z) ...
它不仅删除了所有不必要的空格,还删除了注释。因为它的工作原理是处理单词(token),不再是源代码里面的:a,123,b等。
Rustc编译器做的第一件事就是将源代码分为单词,这使得解析后的工作更容易进行,不必处理诸如1,2,3,这样的个别字符,只需处理诸如“integer literal 123”这样的单词。另外,空白和注释在分词之后就消失了,因为它们对编译器来说没有意义。
stringify!()是一种将一串单词转换回字符串的方法,但它是基于“最佳效果”的:它将单词转换回文本,并且仅在需要时才在单词周围插入空格(以避免将b、c转换为bc)。
所以这是一个死胡同。Rustc不小心把宝贵的空白符丢掉了,但这在Python中非常重要。
我们可以尝试猜测一下哪些代码的空格必须用换行符代替,但是缩进肯定会成为一个问题:
fn main() {let a = stringify!(if False:x()y());let b = stringify!(if False:x()y());dbg!(a);dbg!(b);dbg!(a == b);
}
$ cargo rCompiling scratchpad v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.20sRunning `target/debug/scratchpad`
[src/main.rs:12] a = "if False : x() y()"
[src/main.rs:13] b = "if False : x() y()"
[src/main.rs:14] a == b = true
这两个Python代码片段有不同的含义,但是stringify!给了我们相同的结果。
在放弃之前,让我们尝试一下其他类型的宏。
过程宏
Rust的过程宏是定义宏的另一种方法。尽管macro_rules!只能定义“函数样式的宏”(带有!标记的宏),过程宏也可以定义自定义派生宏(例如#[derive(Stuff)])和属性宏(例如#[stuff])。
过程宏是作为编译器插件实现的。您需要编写一个函数,该函数可以访问编译器看到的单词流,然后就可以执行所需的任何操作,最后需要返回一个新的单词流供编译器使用(或者用于自定义的用途):
#[proc_macro]
pub fn python(input: TokenStream) -> TokenStream {todo!()
}
上述单词流不够好。因为我们需要源代码,而不仅仅是单词。虽然目前还没有成功,但是让我们继续吧,也许过程宏更大的灵活性能够解决问题。
由于过程宏在编译过程中运行Rust代码,因此它们需要使用单独的proc-macro类库中,这个类库在您编译其他内容之前已经被编译好。
$ cargo new --lib python-macroCreated library `python-macro` package
查看python-macro/Cargo.toml:
[lib]
proc-macro = true
查看Cargo.toml:
[dependencies]
python-macro = { path = "./python-macro" }
让我们从一个只有panics (todo!())的实现开始,在输出TokenStream之后:
// python-macro/src/lib.rs
extern crate proc_macro;
use proc_macro::TokenStream;#[proc_macro]
pub fn python(input: TokenStream) -> TokenStream {dbg!(input.to_string());todo!()
}
// src/main.rs
use python_macro::python;fn main() {println!("Hello ...");python! {print("... World!")print("Bye.")}
}
$ cargo rCompiling python-macro v0.1.0Compiling scratchpad v0.1.0
error[E0658]: procedural macros cannot be expanded to statements--> src/main.rs:5:5|
5 | / python! {
6 | | print("... World!")
7 | | print("Bye.")
8 | | }| |_____^|= note: see issue #54727 <https://github.com/rust-lang/rust/issues/54727> for more information= help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
天啊,这里发生了什么?
Rust错误为“ 过程宏不能扩展为语句 ”,以及有关启用“hygienic macros”的内容。Macro hygiene是Rust宏的出色功能,不会意外地将任何名称“泄漏”给外界(反之亦然)。如果宏扩展使用了名为的x的临时变量,则它将与宏外部的任何代码中出现的变量x分开。
但是,此功能对于过程宏还不稳定。因此,过程宏除了作为一个单独的项(例如在文件范围内,但不在函数内)之外,不允许出现在任何地方。
接下来,我们会发现存在一个非常可怕但令人着迷的解决方法—让我们启用实验功能#![feature(proc_macro_hygiene)]并继续我们的冒险。
(如果你将来读到这篇文章时,proc_macro_hygiene已经稳定下来了:你可以跳过最后几段。^ ^)
$ sed -i '1i#![feature(proc_macro_hygiene)]' src/main.rs
$ cargo rCompiling scratchpad v0.1.0
[python-macro/src/lib.rs:6] input.to_string() = "print(\"... World!\") print(\"Bye.\")"
error: proc macro panicked--> src/main.rs:6:5|
6 | / python! {
7 | | print("... World!")
8 | | print("Bye.")
9 | | }| |_____^|= help: message: not yet implementederror: aborting due to previous errorerror: could not compile `scratchpad`.
在向我们展示了它的字符串输入参数之后,我们的过程宏即如预期般地崩溃了:
print("... World!") print("Bye.")
正如预期的那样,空白符再次被丢弃了。:(
是时候选择放弃了。
不过或者..也许有一种方法可以解决这个问题。
重建空白符
尽管rustc编译器只在解析和编译时使用单词,但是在某种程度上它仍然可以准确地知道何时报告错误。单词中没有换行符,但是它仍然知道我们的错误发生在第6到第9行。那它如何做到的?
事实证明,单词中包含很多信息。它们包含一个Span,是单词在源文件中的开始和结束的位置。Span可以告诉单词在哪个文件、行和列编号处开始和结束。
如果我们能够得到这些信息,我们就可以通过在单词之间放置空格和换行符来重新构造空白符,以匹配它们的行和列信息。
提供这些信息的函数还不稳定,而且还没有#![feature(proc_macro_span)]。让我们启用它,看看我们得到了什么:
#![feature(proc_macro_span)]extern crate proc_macro;
use proc_macro::TokenStream;#[proc_macro]
pub fn python(input: TokenStream) -> TokenStream {for t in input {dbg!(t.span().start());}todo!()
}
$ cargo rCompiling python-macro v0.1.0Compiling scratchpad v0.1.0
[python-macro/src/lib.rs:9] t.span().start() = LineColumn {line: 7,column: 8,
}
[python-macro/src/lib.rs:9] t.span().start() = LineColumn {line: 7,column: 13,
}
[python-macro/src/lib.rs:9] t.span().start() = LineColumn {line: 8,column: 8,
}
[python-macro/src/lib.rs:9] t.span().start() = LineColumn {line: 8,column: 13,
}
真棒!我们得到了一些数据。
但是只有四个单词了。原来("... World!") 这里只出现一个单词,而不是三个((,"... World!",和))。如果看一下TokenStream的文档,我们会发现它并没有提供单词流,而是单词树。显然,Rust的词法分析器已经匹配了括号(以及大括号和方括号),并且它不仅给出了线性的单词列表,而且还给出了单词树。括号内的单词可以看成是某个单词组的后代。
让我们修改过程宏以递归地遍历组内的所有单词(并改进一下输出):
#[proc_macro]
pub fn python(input: TokenStream) -> TokenStream {print(input);todo!()
}fn print(input: TokenStream) {for t in input {if let TokenTree::Group(g) = t {println!("{:?}: open {:?}", g.span_open().start(), g.delimiter());print(g.stream());println!("{:?}: close {:?}", g.span_close().start(), g.delimiter());} else {println!("{:?}: {}", t.span().start(), t.to_string());}}
}
$ cargo rCompiling python-macro v0.1.0Compiling scratchpad v0.1.0
LineColumn { line: 7, column: 8 }: print
LineColumn { line: 7, column: 13 }: open Parenthesis
LineColumn { line: 7, column: 14 }: "... World!"
LineColumn { line: 7, column: 26 }: close Parenthesis
LineColumn { line: 8, column: 8 }: print
LineColumn { line: 8, column: 13 }: open Parenthesis
LineColumn { line: 8, column: 14 }: "Bye."
LineColumn { line: 8, column: 20 }: close Parenthesis
符合预期,太棒了!
现在要重建空白符,如果我们不在正确的行中,我们需要插入换行符,如果我们不在正确的列中,则需要插入空格。让我们来看看效果:
#![feature(proc_macro_span)]extern crate proc_macro;
use proc_macro::{TokenTree, TokenStream, LineColumn};#[proc_macro]
pub fn python(input: TokenStream) -> TokenStream {let mut s = Source {source: String::new(),line: 1,col: 0,};s.reconstruct_from(input);println!("{}", s.source);todo!()
}struct Source {source: String,line: usize,col: usize,
}impl Source {fn reconstruct_from(&mut self, input: TokenStream) {for t in input {if let TokenTree::Group(g) = t {let s = g.to_string();self.add_whitespace(g.span_open().start());self.add_str(&s[..1]); // the '[', '{' or '('.self.reconstruct_from(g.stream());self.add_whitespace(g.span_close().start());self.add_str(&s[s.len() - 1..]); // the ']', '}' or ')'.} else {self.add_whitespace(t.span().start());self.add_str(&t.to_string());}}}fn add_str(&mut self, s: &str) {// Let's assume for now s contains no newlines.self.source += s;self.col += s.len();}fn add_whitespace(&mut self, loc: LineColumn) {while self.line < loc.line {self.source.push('\n');self.line += 1;self.col = 0;}while self.col < loc.column {self.source.push(' ');self.col += 1;}}
}
$ cargo rCompiling python-macro v0.1.0Compiling scratchpad v0.1.0print("... World!")print("Bye.")
error: proc macro panicked
看来这是行得通的,但是这些额外的换行符和空格又是怎么回事?对比下源文件,这是对的,第一个标记从第7行第8列开始,因此它正确地将print放在第8列的第7行。我们要查找的位置正是.rs文件中的确切位置。
开始时多余的换行符不是问题(空行在Python中无效)。它甚至具有很好的副作用:当Python报告错误时,它报告的行号将与.rs文件中的行号匹配。
但是,这8个空格是个问题。尽管我们内部的Python代码python!{..}相对于Rust代码是适当缩进的,但我们提取的Python代码应以“零”缩进级别开始。否则,Python将发生无效缩进的错误。
让我们从所有列号中减去第一个标记的列号:
start_col: None,// <snip>start_col: Option<usize>,// <snip>let start_col = *self.start_col.get_or_insert(loc.column);let col = loc.column.checked_sub(start_col).expect("Invalid indentation.");while self.col < col {self.source.push(' ');self.col += 1;}// <snip>
$ cargo rCompiling python-macro v0.1.0Compiling scratchpad v0.1.0print("... World!")
print("Bye.")
error: proc macro panicked
结果太棒了!
现在,我们只需要把这个字符串转换为字符串文字标记 并将其放在run_python();周围即可:
TokenStream::from_iter(vec![TokenTree::from(Ident::new("run_python", Span::call_site())),TokenTree::Group(Group::new(Delimiter::Parenthesis,TokenStream::from(TokenTree::from(Literal::string(&s.source))),)),TokenTree::from(Punct::new(';', Spacing::Alone)),])
太糟糕了,直接使用TokenTree太困难了,尤其是从头开始制作trees和streams。
如果只有一种方法可以编写我们要生成的Rust代码,那就只能是quote类库的quote!宏:
let source = s.source;quote!( run_python(#source); ).into()
现在使用我们的原始run_python函数对其进行测试:
#![feature(proc_macro_hygiene)]
use python_macro::python;fn run_python(code: &str) {let py = pyo3::Python::acquire_gil();if let Err(e) = py.python().run(code, None, None) {e.print(py.python());}
}fn main() {println!("Hello ...");python! {print("... World!")print("Bye.")}
}
$ cargo rCompiling scratchpad v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.31sRunning `target/debug/scratchpad`
Hello ...
... World!
Bye.
终于成功了!
封装成类库
现在我们把它变成一个可重用的库:
删除fn main,
重命名main.rs为lib.rs,
给类库起个好名字,例如inline-python,
公开run_python,
更改quote!()中的run_python调用改为::inline_python::run_python,同时添加pub python_macro::python;从python!这个类库中重新导出宏。
下一步计划
可能还有很多内容需要改进,还有很多错误需要发现,但是至少我们现在可以在Rust代码行之间运行Python片段了。
目前最大的问题是,这还不是很有用,因为没有数据可以(轻松)越过Rust-Python的边界。
在第2部分中,我们将研究如何使Rust变量用于Python代码。
更新:在等待第2部分的同时,还有第1A部分,只是它没有改进我们的python!{}宏,但涉及了人们向我询问的一些细节。具体来说,它涉及:
为什么要像这样在Rust内部使用Python,
语法问题,例如使用Python的单引号字符串
使用Span::source_text的选项,当我第一次编写这段代码时,它其实还不存在。
原文链接:
https://blog.m-ou.se/writing-python-inside-rust-1/
推荐阅读
360金融首席科学家张家兴:别指望AI Lab做成中台
我们想研发一个机器学习框架,6 个月后失败了
八年,腾讯优图攒了多厚的技术“家底”?
无需训练 RNN 或生成模型,如何编写一个快速且通用的 AI “讲故事”项目?
区块链重大技术分析:IBM、微软、苹果、Google 都做了什么?
你点的每个“在看”,我都认真当成了AI
相关文章:

Docker配置指南系列(二):指令集(二)
pause: 停止一个容器的所有进程语法:ocker pause CONTAINER [CONTAINER...] port: 列出容器的端口映射,或者查看指定开放端口的NAT映射语法:docker port [--help] CONTAINER [PRIVATE_PORT[/PROTO]] ps: 列出容器语法࿱…
无需训练RNN或生成模型,我写了一个AI来讲故事
作者 | Andre Ye译者 | 弯月出品 | AI科技大本营(ID:rgznai100)这段日子里,我们都被隔离了,就特别想听故事。然而,我们并非对所有故事都感兴趣,有些人喜欢浪漫的故事,他们肯定不喜欢…
Java字节码instrument研究
MyAgent项目 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.…

怎样保持良好的心态
有一位朋友有一次气冲冲的跟我说:“气死我了!我刚刚发现我一位员工出了错,令产品出现了质量的问题,我修理了他一顿。。。 我问:”你认为你的生产流程里面可能一点错误都没有吗?“ 他说:”应该不…

web编程速度大比拼(nodejs go python)(非专业对比)
C10K问题的解决,涌现出一大批新框架,或者新语言,那么问题来了:到底谁最快呢?非专业程序猿来个非专业对比。 比较程序:输出Hello World! 测试程序:siege –c 100 –r 100 –b 例子包括࿱…

linux邮件服务
邮件服务要求:l 能够构建完整的邮件系统 能够正确设置DNS邮件服务器记录 l 能够配置sendmail服务器 设置客户端软件使用邮件服务器 准备工作: l 主机名:srv.benet.com /etc/sysconfig/network <永久的> l 域名 正向区域 bt.com完成NDS的…
MaskFlownet:基于可学习遮挡掩模的非对称特征匹配丨CVPR 2020
来源 | 微软研究院AI头条(ID: MSRAsia)编者按:在光流预测任务中,形变带来的歧义与无效信息会干扰特征匹配的结果。在这篇 CVPR 2020 Oral 论文中,微软亚洲研究院提出了一种可学习遮挡掩模的非对称特征匹配模块 &#x…

GDB调试--以汇编语言为例
#rpm -qa |grep gdb 下载: 安装 #tar -zxvf #./configure #make 使用GDB 以汇编语言调试为例 汇编语言实现CPUID指令 CPUID cpuid是Intel Pentinum以上级CPU内置的一个指令(486级以下的CPU不支持),他用于识别某一类型…

汇编语言系统调用过程
以printf为例,详细解析一个简单的printf调用里头,系统究竟做了什么,各寄存器究竟如何变化。 如何在汇编调用glibc的函数?其实也很简单,根据c convention call的规则,参数反向压栈,call…

switch语句中在case块里声明变量会遇到提示“Expected expression before...的问题
switch语句中在case块里声明变量会遇到提示“Expected expression before..."的问题 例如在如下代码中 1case constant:2 int i 1;3 int j 2;4 self.sum i j;5 break;GCC在case语句之后的第一行中声明变量时遇到问题。 这时需要在case块两端添加花括号&am…
帮AI体检看病一条龙服务,阿里发布“AI安全诊断大师”
如同一些出生免疫力就有缺陷的人一样,AI也存在免疫力缺陷。基于从源头打造安全免疫力的新一代安全架构理念,最近,阿里安全研究发布了一项核心技术“AI安全诊断大师”,可对AI模型全面体检,“看诊开方”,让AI…

Spring学习总结(7)——applicationContext.xml 配置文详解
web.xml中classpath:和classpath*: 有什么区别? classpath:只会到你的class路径中查找找文件; classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找. 存放位置: 1:src下面 需要在web.xml中定义如下&…

GDB查看栈信息
栈:是程序存放数据内存区域之一,特点是LIFO(后进先出)。 PUSH:入栈 POP:出战 使用场景: 1.保存动态分配的自动变量使用栈 2.函数调用时,用栈传递函数参数,半寸返回地址…

数据库学习之路
今天迎来入冬的第二场雪,闲来无事就整理了下总结下工作以来所有数据库方面的书籍和资料,发现了不少,很多已经读过或者正在读的书籍,oracle真的很强大,直到现在发现才入门的水平,当然很多书读一遍是不行的&a…
为什么铺天盖地都是Python的广告?
最近,知乎关于Python有一个热议问题: 甚至在抖音上,笔者有一次还看到Python占领了热搜!应该有很多不懂技术的吃瓜群众也被Python的热度炒懵了……但是,Python真的值得学吗?真的值得花这么多钱去学吗&#x…

python3正则表达式符号和用法
转载于:https://www.cnblogs.com/wumac/p/5441322.html
从寄存器看I386和x64位中函数调用中参数传递
x86_64基本使用寄存器存储函数参数,寄存器不够才入栈; 而i386将所有参数保存在栈上,通过gcc的扩展功能__attribute__((regparm()))即可实现部分参数的寄存器传递。 调试语法: --《深入理解计算机系统(原书第2版)》 代码 #incl…

转:去掉Flex4生成的SWF加载时的进度条
方法一: <?xml version"1.0" encoding"utf-8"?> <s:Application xmlns:fx"http://ns.adobe.com/mxml/2009" xmlns:s"library://ns.adobe.com/flex/spark" xmlns:mx"library://ns.adobe.com/f…
饿了么交易系统5年演化史
作者 | 挽晴来源 | 阿里巴巴中间件(ID:Aliware_2018)个人简介:2014年12月加入饿了么,当时参与后台系统的研发(WalisJavis>Walle),主要面向客服和BD。2015年5月开始接触订单系统的研发,7月负责订单研发组…

Python迁移MySQL数据到MongoDB脚本
MongoDB是一个文档数据库,在存储小文件方面存在天然优势。随着业务求的变化,需要将线上MySQL数据库中的行记录,导入到MongoDB中文档记录。 一、场景:线上MySQL数据库某表迁移到MongoDB,字段无变化。 二、Python模块&am…

使用valgrind分析C程序调用线路图
Valgrind可以检测内存泄漏和内存违例,但是用Valgrind生成的日志信息结合kcachegrind就可以查看C程序的执行线路图,调用时间,是做性能优化和查看代码的非常好的工具。 1.下载安装 Valgrind 安装 到www.valgrind.org下载最新版valgrind # wg…

纯CSS实现蓝色圆角下拉菜单
代码简介: 这个菜单没有使用任何的图片,完全是用CSS实现的,包括圆角效果也同样是,而且还考虑了多浏览器的兼容性,可以说非常不错,既兼容性好,又外观漂亮,下拉导航菜单目前比较流行&a…
生产型机器学习已经没那么困难了?
作者 | Caleb Kaiser译者 | 香槟超新星出品 | CSDN(ID:CSDNnews)封面图源自视觉中国在软件工程的诸多领域内,生产用例是相当标准化的。以Web开发为例,要在Web应用中实现身份认证,你不会去创造一个数据库,自…

poj1330Nearest Common Ancestors 1470 Closest Common Ancestors(LCA算法)
LCA思想:http://www.cnblogs.com/hujunzheng/p/3945885.html 在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,非常好的处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这…
干货 | 时间序列预测类问题下的建模方案探索实践
作者 | 陆春晖责编 | Carol出品 | AI科技大本营(ID:rgznai100)背景时间序列类问题是数据分析领域中一类常见的问题,人们有时需要通过观察某种现象一段时间的状态,来判断其未来一段时间的状态。而时间序列就是该种现象某一个统计指…
Redis安装与源码调试
linux版本:64位CentOS 6.5 Redis版本:redis-3.0.6 (更新到2016年1月22日) Redis官网:http://redis.io/ Redis常用命令:http://redis.io/commands 1.安装Redis # wget http://download.redis.io/releases/redis-3.2.6.tar.g…

system pause in C#
方法一: Console.Write("Press any key to continue . . . "); Console.ReadKey(true); 注:也可用ReadLine()或Read(),但是只能对回车进行响应,不能达到anykey的效果。 方法二: 1) 在源文件using处加入using…

C#设置当前程序通过IE代理服务器上网
注意:以下设置只在当前程序中有效,对IE浏览器无效,且关闭程序后,自动释放代码。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices;public static …

计算机科学精彩帖子收集
linux源码 LXR 源自“the Linux Cross Referencer”,中间的“X”形象地代表了“Cross”。与 Source Navigator 类似,它也是分析阅读源代码的好工具。不同的是,它将源代码借助浏览器展示出来,文件间的跳转过程成了我熟悉的点击超链…
挑战王者荣耀“绝悟” AI,我输了!
作者 | 马超责编 | 伍杏玲出品 | CSDN(ID:CSDNnews)腾讯 AI Lab 与王者荣耀联合研发的策略协作型AI,“绝悟”首次开放大规模开放:5月1日至4日,玩家从王者荣耀大厅入口,进入“挑战绝悟”测试&…