会的计算机语言不多,十来种吧。个人看法,python的语法很棒棒啊。
优点一:循环遍历的写法简洁、优雅。
for i in range(100):
do_something(i)
for idx,item in enumerate(anything_that_can_enumerate):
do_something(idx,item)
strng = strng[::-1] #reverse a string
strng = ''.join(c for c in strng if c.isalpha()) # filt a string, drop none alpha chars
如果用其他语言,多半写不出这么简洁优雅的。
优点二:抽象程度高,多态强悍,库代码复用良好
比如计算常用到的分数库、定点数库,Fraction和Decimal类型可以简单地互相转换。这个在动态语言当中,真心是做得很好的了。
优点三:battery included
通常这不能算是语法层面的优点。然而python自带的库,覆盖广、质量高。类似的事情,大家都用同一款库,无疑减少了歧义、差错的机会。
缺点一:语法糖多,容易造成混淆
比较容易写出简短而难理解的代码(当然,不会比perl更糟糕)。
缺点二:解释器不太关心代码的正确性,更依赖程序员本人
比如说,函数可以有时候有返回值有时候没有。丢弃个返回值可以随意。对尾递归没有优化,递归容易溢出、效率也低。等于说对递归的支持,比一些其他的语言不过是半成品。
这个问题下面,好多瞎吐槽的观点:
1、游标卡尺哏。这个根本不是问题。就算语言不强制,缩进排版不严格执行的程序员,都应该拉出来打。那些学生物、统计的,用起python来,能整整齐齐的缩进,绝对是好事。
2、动态类型问题。动态类型是一种设计、选择,并不能说是缺陷。据此说python不太适合大型项目的,请问,有哪个动态类型语言比python更适合大型项目?js?
3、变量名称简化哏。python风格允许一些一望而知、局部使用的变量使用简短的名称,但绝对不滥用简化原则,在标准库里也是同样。像n代表数量,i代表循环计数变量。都是允许的。然而说def xx(a,b): 这样的,不能怨python。在go里面,func xx(a,b interface{}) []interface{} 这样的函数声明,一样也是符合语法规范的。编程的人真心想写烂代码,用什么语言都可以。
第一次参与回答这种问题,鉴于评论区有个拿个别语言特性反复争论的。还是列几段代码,旁观的自行比对,看看不一样的风格吧。举例Codewars上很简单一道题目,和几种语言当中网友的解(选择高赞当中比较简短的)。先说明,这几种解的代码,不是完全对等的翻译,是不同的人做出的等价的解而已,代码风格都未见得是怎么特别好。不过不同语言的语法差别可见一斑。
先看问题的定义:
The aim of the kata is to decompose n! (factorial n) into its prime factors.
Examples:
n = 12; decomp(12) -> "2^10 * 3^5 * 5^2 * 7 * 11"
since 12! is divisible by 2 ten times, by 3 five times, by 5 two times and by 7 and 11 only once.
n = 22; decomp(22) -> "2^19 * 3^9 * 5^4 * 7^3 * 11^2 * 13 * 17 * 19"
n = 25; decomp(25) -> "2^22 * 3^10 * 5^6 * 7^3 * 11^2 * 13 * 17 * 19 * 23"
Prime numbers should be in increasing order. When the exponent of a prime is 1 don't put the exponent.
Notes
the function is decomp(n) and should return the decomposition of n! into its prime factors in increasing order of the primes, as a string.
factorial can be a very big number (4000! has 12674 digits, n will go from 300 to 4000).
In Fortran - as in any other language - the returned string is not permitted to contain any redundant trailing whitespace: you can use dynamically allocated character strings.
Python是这样的风格:
def decomp(n):
f = {}
for i in range(2, n+1):
for j in range(2, int(i**0.5)+1):
while i%j==0:
i = i//j
f[j] += 1
if i!=1:
if i in f: f[i] += 1
else: f[i] = 1
return ' * '.join(["{}^{}".format(i, f[i]) if f[i]>1 else str(i) for i in sorted(f)])
Swift最短是这样的风格:
import Foundation
func decomp(_ m: Int) -> String {
return (1...m).reduce(into: Array(repeating: 0, count: m + 1)) { arr, num in
var i = 2, num = num
while num > 0 && i <= num {
if num % i != 0 { i += 1 }
else { arr[i] += 1; num /= i }
}
}.enumerated()
.filter { $1 != 0 }
.map { $1 == 1 ? "\($0)" : "\($0)^\($1)" }
.joined(separator: " * ")
}
R的解当中,最简短的是这样:
primes=c(2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601,1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,1697,1699,1709,1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789,1801,1811,1823,1831,1847,1861,1867,1871,1873,1877,1879,1889,1901,1907,1913,1931,1933,1949,1951,1973,1979,1987,1993,1997,1999,2003,2011,2017,2027,2029,2039,2053,2063,2069,2081,2083,2087,2089,2099,2111,2113,2129,2131,2137,2141,2143,2153,2161,2179,2203,2207,2213,2221,2237,2239,2243,2251,2267,2269,2273,2281,2287,2293,2297,2309,2311,2333,2339,2341,2347,2351,2357,2371,2377,2381,2383,2389,2393,2399,2411,2417,2423,2437,2441,2447,2459,2467,2473,2477,2503,2521,2531,2539,2543,2549,2551,2557,2579,2591,2593,2609,2617,2621,2633,2647,2657,2659,2663,2671,2677,2683,2687,2689,2693,2699,2707,2711,2713,2719,2729,2731,2741,2749,2753,2767,2777,2789,2791,2797,2801,2803,2819,2833,2837,2843,2851,2857,2861,2879,2887,2897,2903,2909,2917,2927,2939,2953,2957,2963,2969,2971,2999,3001,3011,3019,3023,3037,3041,3049,3061,3067,3079,3083,3089,3109,3119,3121,3137,3163,3167,3169,3181,3187,3191,3203,3209,3217,3221,3229,3251,3253,3257,3259,3271,3299,3301,3307,3313,3319,3323,3329,3331,3343,3347,3359,3361,3371,3373,3389,3391,3407,3413,3433,3449,3457,3461,3463,3467,3469,3491,3499,3511,3517,3527,3529,3533,3539,3541,3547,3557,3559,3571,3581,3583,3593,3607,3613,3617,3623,3631,3637,3643,3659,3671,3673,3677,3691,3697,3701,3709,3719,3727,3733,3739,3761,3767,3769,3779,3793,3797,3803,3821,3823,3833,3847,3851,3853,3863,3877,3881,3889,3907,3911,3917,3919,3923,3929,3931,3943,3947,3967,3989)
factors = function(n){
prime_count=1
new_wector=NULL
while (n!=1) {
if(n%%primes[prime_count]==0){
n=n/primes[prime_count]
new_wector=c(new_wector,primes[prime_count])
}
else{
prime_count=prime_count+1
}
}
a=new_wector
return(a)
}
decomp <- function(n) {
n=seq(2,n,1)
b=unlist(lapply(n,factors))
c=unique(b)
e=as.vector(table(b))
d=paste0(c,"^",e,' * ',collapse = '')
f=gsub("\\^1 \\*"," *",d)
e=substr(f,start = 1,stop=nchar(as.character(f))-3)
return(e)
}
Rust的解当中,比较简洁的是这样的:
usestd::collections::BTreeMap;fn factorize(n: i32)-> Option<(i32,i32)>{letmutp=2;loop{ifp*p>n{returnNone;}ifn%p==0{returnSome((n/p,p));}p+=ifp==2{1}else{2};}}fn decomp(n: i32)-> String {ifn<=1{returnn.to_string()}letmutfactors=BTreeMap::new();foriin2..=n{letmutn=i;whileletSome((r,p))=factorize(n){factors.entry(p).and_modify(|e|*e+=1).or_insert(1);n=r;}factors.entry(n).and_modify(|e|*e+=1).or_insert(1);}factors.iter().map(|(&p,&e)|{ife==1{p.to_string()}else{format!("{}^{}",p,e)}}).collect::>().join(" * ")}
Go的解当中最简洁的是这样的:
package kata
import (
"strings"
"fmt"
)
func Decomp(n int) string {
primes := make(map[int]int, 0)
for i := 2; i <= n; i++ {
j, k := 2, i
for j * j <= i {
if k % j == 0 {
primes[j]++
k /= j
} else {
j++
}
}
primes[k]++
}
b := new(strings.Builder)
for i := 2; i <= n; i++ {
if primes[i] > 1 {
fmt.Fprintf(b, "%d^%d * ", i, primes[i])
} else if primes[i] == 1 {
fmt.Fprintf(b, "%d * ", i)
}
}
s := b.String()
return s[:len(s) - 3]
}
Julia的比较简洁的解是这样的:
module Fact
export decomp
function create_string_output(factors::Array{Pair{Int64,Int64},1})::String
stringify(pair::Pair) = if pair[2] > 1 "$(pair[1])^$(pair[2])" else "$(pair[1])" end
return [stringify(x) for x in factors] |> x -> join(x, " * ")
end
function largest_power(n::Int64, p::Int64)::Int64
x = 0
while (n > 0)
n/=p
x += trunc(Int, n)
end
return x
end
function is_prime(n::Int64)::Bool
if n <= 1 return false end
for i in 2:(n-1) if n%i == 0 return false end end
return true
end
function decomp(n::Int64)::String
count_map = Dict{Int64, Int64}()
possible_prime::Int64 = 2
exponent::Int64 = -1
still_running = true
while (exponent != 0)
exponent = largest_power(n, possible_prime)
is_valid_prime = is_prime(possible_prime) && (exponent>0)
if is_valid_prime count_map[possible_prime] = exponent end
possible_prime+=1
end
pairs = collect(count_map) |> pair_array -> sort(pair_array, by= x -> x[1])
return create_string_output(pairs)
end
end
看这个Haskell的。去掉Import、注释,的确比上面的python解短。算不算简洁易懂,大家自己判断吧:
module FactorialDecomposition.Kata (decomp) where
import Data.List
import qualified Data.Map.Strict as Map
decomp :: Int -> String
decomp n = intercalate " * " $ Map.elems $ Map.mapWithKey (\k v -> (show k) ++ (if v > 1 then "^" ++ show v else "")) $ foldl (\m -> Map.unionWith (+) m . primeFactors) Map.empty [2..n] where
-- Helper function - decompose integer into prime factors
-- Adapted from https://www.codewars.com/kata/reviews/58799ea408d0c8b22a000107/groups/58a4a1704aac53766e00141a
primeFactors :: Int -> Map.Map Int Int
primeFactors 0 = Map.empty
primeFactors 1 = Map.empty
primeFactors n = Map.insertWith (+) first 1 $ primeFactors (div n first) where
first = head $ [ x | x <- [2..n], mod n x == 0]
以上不过是语言风格对比,较真其中的细节恐怕就没啥意思了。最后,为了防杠精又来说,有用别的语言行数更短的解法(确实有),我改下上面的python解,不过于损失可读性的情况下,给一个更简短的解,9行吧(当然还能更短,Codewars上有个3行的解):
def decomp(n):
f = {}
for i in range(2, n+1):
for j in range(2, int(i**0.5)+1):
while i%j==0:
i,f [j] = i//j, f[j]+1 if j in f else 1
if i > 1:
f[i] = f[i]+1 if i in f else 1
return ' * '.join("{}^{}".format(i, f[i]) if f[i]>1 else str(i) for i in sorted(f))