Programski jezik Zig

  • Začetnik teme Začetnik teme bmaxa
  • Datum pokretanja Datum pokretanja

bmaxa

Legenda
Poruka
70.808
Zig je novi jezik, koji ima za nameru da zameni C.
Inspiracija je C,Rust i Go.
Dok je Go jezik viseg nivoa koji ima za cilj da zameni Python,
kao kompajlirani jezik samim tim i efikasniji, Rust tezi da
zameni C++ kao sigurniji jezik, Zig zeli da zameni C uzimajuci
elemente i Rust-a i Go-a.
Dakle, iz Rust-a je uzet error handling(tagovane unije) a iz Go-a,
neke konstrukcije kao sto su gorutine ovde implementirane
kao korutine(async/await), pa i defer koji dodje kao zamena za C++ RAII.
Jezik ne koristi C lib(libc) nego ima svoj, tako da je mali.
Interesantna stvar je da strukture identifikuju tip sa imenom
varijable koja dobija vrednost iste, ili f-je koja ih vraca.
Potom nema odredjeni alokator vec se to uzima iz biblioteke,
po izboru i potrebi.
Vise o jeziku: https://ziglang.org/documentation/master/
Na kraju da dam dva primera kratkih programa, pa onako da
se stvori neki utisak.
Prvi je program k-nucleotide sa computer language benchmark
strane.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/description/knucleotide.html#knucleotide
Kod:
const std = @import("std");
const FACTOR: u32 = 16;
const HashMap = std.StringHashMap(u32);
const Val = struct { key: u32, value: []const u8 };

fn kvLessThan(_: void, lhs: Val, rhs: Val) bool {
return lhs.key > rhs.key;
}
var def = HashMap.Entry{ .key = "", .value = 0 };
fn calculate(allocator: *std.mem.Allocator, poly: []const u8, size: u32) !HashMap {
var hash = HashMap.init(allocator);
const A = struct {
allocator: *std.mem.Allocator,
poly: []const u8,
size: u32,
i: u32,
hash: HashMap,
const Self = @This();
fn ex(self: *Self) void {
self.hash = HashMap.init(self.allocator);
{
var key: u64 = 0;
var j: u32 = self.i;

while (j < self.poly.len + 1 - self.size) : (j += FACTOR) {
var entry = self.hash.getOrPutValue(self.poly[j .. j + self.size], 0) catch &def;
entry.value += 1;
}
}
}
};
var i: u32 = 0;
var frame: [FACTOR]A = undefined;
var threads: [FACTOR]*std.Thread = undefined;
while (i < FACTOR) : (i += 1) {
frame[i] = .{
.allocator = allocator,
.poly = poly,
.size = size,
.i = 0,
.hash = undefined,
};
frame[i].i = i;
threads[i] = try std.Thread.spawn(&frame[i], A.ex);
}
i = 0;
while (i < FACTOR) : (i += 1) {
threads[i].wait();
var ht = frame[i].hash;
defer ht.deinit();
var iterator = ht.iterator();
while (iterator.next()) |entry| {
var e = try hash.getOrPutValue(entry.key, 0);
e.value += entry.value;
}
}
return hash;
}
fn generateFrequenciesForLength(allocator: *std.mem.Allocator, poly: []const u8, comptime desired_length: usize, output: []u8) !void {
var hash = try calculate(allocator, poly, desired_length);
defer hash.deinit();
var list: []Val = try allocator.alloc(Val, hash.count());
defer allocator.free(list);

var i: usize = 0;
var it = hash.iterator();
while (it.next()) |entry| {
list[i] = .{ .key = entry.value, .value = entry.key };
i += 1;
}

std.sort.sort(Val, list, {}, kvLessThan);

var position: usize = 0;
for (list) |*entry| {
const slice = try std.fmt.bufPrint(output[position..], "{} {d:.3}\n", .{
entry.value,
100.0 * @intToFloat(f64, entry.key) / @intToFloat(f64, poly.len - desired_length + 1),
});
position += slice.len;
output[position] = 0;
}
}

fn generateCount(allocator: *std.mem.Allocator, poly: []const u8, comptime olig: []const u8, output: []u8) !void {
var hash = try calculate(allocator, poly, olig.len);
defer hash.deinit();

{
const count = if (hash.getEntry(olig)) |entry| entry.value else 0;
const slice = try std.fmt.bufPrint(output, "{}\t{}", .{ count, olig });
output[slice.len] = 0;
}
}
pub fn main() anyerror!void {
var allocator = std.heap.c_allocator;

var stdout_file = std.io.getStdOut();
var stdout_out_stream = stdout_file.outStream();
var buffered_stdout = std.io.bufferedOutStream(stdout_out_stream);
defer _ = buffered_stdout.flush() catch {};
const stdout = buffered_stdout.writer();

var stdin_file = std.io.getStdIn();
var stdin_in_stream = stdin_file.inStream();
var buffered_stdin = std.io.bufferedInStream(stdin_in_stream);
const stdin = buffered_stdin.reader();

var buffer: [4096]u8 = undefined;

while (true) {
const line = try stdin.readUntilDelimiterOrEof(buffer[0..], '\n');
if (line) |line1| {
if (std.mem.startsWith(u8, line1, ">THREE")) {
break;
}
} else {
break;
}
}

var poly = std.ArrayList(u8).init(allocator);
defer poly.deinit();

while (true) {
const line = try stdin.readUntilDelimiterOrEof(buffer[0..], '\n');
if (line) |line1| {
for (line1) |c| {
try poly.append(std.ascii.toUpper(c));
}
} else {
break;
}
}

const poly_shrunk = poly.toOwnedSlice();

const counts = [_]u8{ 1, 2 };
const entries = [_][]const u8{ "GGT", "GGTA", "GGTATT", "GGTATTTTAATT", "GGTATTTTAATTTATAGT" };

var output: [counts.len + entries.len][4096]u8 = undefined;

inline for (counts) |count, i| {
try generateFrequenciesForLength(allocator, poly_shrunk, count, output[i][0..]);
}

inline for (entries) |entry, i| {
try generateCount(allocator, poly_shrunk, entry, output[i + counts.len][0..]);
}

for (output) |entry| {
try stdout.print("{s}\n", .{entry[0..]});
}
}

Kompajlira se sa: zig build-exe k-nucleotide.zig -O ReleaseFast --library c

Drugi program je primer servera i klijenta, koji koristi async pozive.
Async su ovde korutine, znaci bez obzira sto deluje da ima vise threadova,
zapravo se sve odvija u jednom threadu.
Interesantna stvar je da bi se dobio non blocking io mora se definisati
Kod:
pub const io_mode = .evented;
ispred main-a. Kod je prilicno ilustrativan, tako da mislim da je sve jasno.
Inace async/await je masovno popularno tako da skoro nema novog jezika ko to nema ili
nije skoro uveo.

Kod:
const std = @import("std");
const net = std.net;
const mem = std.mem;
const testing = std.testing;

pub const io_mode = .evented;
pub fn main() !void {
if (!std.io.is_async) return error.SkipZigTest;

// Ignore sigpipe
var act = std.os.Sigaction{
.sigaction = std.os.SIG_IGN,
.mask = std.os.empty_sigset,
.flags = 0,
};
std.os.sigaction(std.os.SIGPIPE, &act, null);
// TODO doing this at comptime crashed the compiler
const localhost = try net.Address.parseIp("127.0.0.1", 6666);

var server = net.StreamServer.init(net.StreamServer.Options{ .reuse_address = true });
defer server.deinit();
try server.listen(localhost);

var server_frame = async testServer(&server);
var client_frame = async testClient(server.listen_address);

try await server_frame;
try await client_frame;
}

fn testClient(addr: net.Address) anyerror!void {
const socket_file = try net.tcpConnectToAddress(addr);
defer socket_file.close();

var buf: [100]u8 = undefined;
const len1 = try socket_file.write("abcd");
const len = try socket_file.read(&buf);
const msg = buf[0..len];
testing.expect(mem.eql(u8, msg, "hello from server\n"));
}

fn testServer(server: *net.StreamServer) anyerror!void {
while (true) {
var client = try server.accept();
var frame = async handle(client);
resume frame;
await frame catch |err| {
std.debug.warn("Disconnected {}: {}\n", .{ client, err });
};
}
}

fn handle(client: anytype) !void {
defer client.file.close();
const stream = client.file.outStream();
const readstream = client.file.inStream();
suspend;
var buf: [256]u8 = undefined;
_ = try readstream.read(buf[0..]);
_ = try stream.print("hello from server\n", .{});
}

Kompajlira se sa: zig build-exe test.zig -O ReleaseFast
Sve je kompajlirano na Linux-a ali bi trebalo da radi i na Windows-u (ili ne znam).
U svakom slucaju prvi put da vidim jezik koji ima za cilj da zameni C.
 
Vidim ja da programerima koji su pisali ove programske jezike bilo baš dosadno u životu. Jedino što ne bih voleo jeste da se stvari zakomplikuju. Obično od standardnog jezika kad prave ili nov ili framework, oni ga tako zakomplikuju da mi se gadi što su uopšte pokušavali.
 
Vidim ja da programerima koji su pisali ove programske jezike bilo baš dosadno u životu. Jedino što ne bih voleo jeste da se stvari zakomplikuju. Obično od standardnog jezika kad prave ili nov ili framework, oni ga tako zakomplikuju da mi se gadi što su uopšte pokušavali.
C ima strahovito ruzne api-je, mislim da u Zigu to moze bolje ;)
No opet problem je u tome sto svi jezici prave vezu sa C, tako da je to nemoguca misija, no nadam se da posle ovoliko godina,
ono sto C++ nije iskoristio, je da se naprave elegantni api-ji za libove. Uzmi recimo da radis WINAPI ili neki od M$ libova koji imaju
C api kao sto je XFS;)
Prva stvar koju radis je da uprostis tu kompleksnost i to je vecma sto su ljudi radili u C++ ;)
Zig ima taj mehanizam da moze da se miksuje sa C, pa tako tranzicija ne mora odmah i sada, a jezik nit je OO nit je funkcionalan,
ali ima fore koje omogucavaju manje glavobolje nego sto je to sa C ;)
 
Zig nije OOP? Kako ću živeti bez toga. :D
Pazi OOP je out of fashion ;)
Fora je da je upravo ta kompleksnost i duboke hijerarhije klasa izazvala to da se smatra
da vise od jednog sloja ne treba. Tu je i antipattern onion programming veliki problem.
To ljustenje luka dok dodjes do funkcionalnosti je takodje izazvalo da OOP bude malo
on hold :P
Tako da ni Rust ni Go ni Zig nemaju klase ;)
Ono sto Zig nema jos su interfejsi. Znaci bas je kao C. Medjutim koristi notaciju za pozive f-ja
kao u OOP ali to je cisto syntactic sugar ;)
 
Hmmm, veoma neobičan pristup. Ja sam navikao na OOP i u mojoj glavi se to pokazuje praktičnim i razumljivim za rad. Ali kada čujem da je to oguljeno, postavljam pitanje kako da uopšte funkcionišem bez klasa.

Ovaj deo mi pojasni samo.
Pa notacija varijabla.funkcija(); umesto funkcija(varijabla);
Sto se tice OOP zakljucili su da je bolje imati samo interfejse nego konkretnu hijerarhiju, tj smatra se da nasledjivanje konkretnih klasa nije dobro,
vise se ide na agregaciju.
 
Pa notacija varijabla.funkcija(); umesto funkcija(varijabla);
Sto se tice OOP zakljucili su da je bolje imati samo interfejse nego konkretnu hijerarhiju, tj smatra se da nasledjivanje konkretnih klasa nije dobro,
vise se ide na agregaciju.
Dobro, js ima tako nesto. Ali tek posto je funkcija definisana. To je prakticno. Samo kako ce koristiti
varijabla.func(), a da se prvo ne definise func()?
 

Back
Top