【问题标题】:How do you loop over the indexes of a string?你如何遍历字符串的索引?
【发布时间】:2018-01-08 05:09:24
【问题描述】:

考虑:

val example = "1234567"

fn digit(c: char): int =
  case- c of
  | '0' => 0 | '1' => 1 | '2' => 2 | '3' => 3 | '4' => 4
  | '5' => 5 | '6' => 6 | '7' => 7 | '8' => 8 | '9' => 9

fn f(): int = loop(0, 0) where {
  fun loop(i: int, acc: int): int =
    if example[i] = '\000' then acc else
    loop(i + 1, acc + digit(example[i]))
}

implement main0() = () where {
  val () = println!("f: ", f())
}

这(尝试)循环遍历字符串的索引,将字符串的字符求和为数字。我已经用.foldleftstreamize_string_char 解决了几个类似的问题,但实际任务需要对索引本身进行数学运算(即,不是使用每个字符,它应该只使用一个字符,如果i+10 处的字符为偶数)。

实际上数学是相关的,因为它似乎强制$UNSAFE.cast2int,因为strlen(input)的结果没有除法运算符:

fn day2(): uint = loop(input, 0, 0) where {
  val len = $UNSAFE.cast2int(strlen(input))
  fn nextindex(i: int): int = (i + len/2) mod len
  fn get(i: int): char = input[i]  // <-- also broken at this point
  // this next line is just me slowly going mad
  fun loop{n:int}{i:nat | i <= n}(s: string(n), i: size_t(i), acc: uint): uint =
    if i >= len then acc else
    if s[i] = s[nextindex(i)] then loop(i+1, acc + digit(s[i])) else
    loop(i+1, acc)
}

f()上面应该怎么写?请给我一个函数示例,该函数循环遍历字符串的索引并按索引从字符串中获取字符。同样,我不需要像

这样的解决方案
typedef charint = (char, int)
fn day1(): int = sum where {
  val lastchar = input[strlen(input)-1]
  val res = input.foldleft(TYPE{charint})((lastchar, 0), (lam((last, sum): charint, c: char) =>
                if last = c then (c, sum + digit(c)) else (c, sum)))
  val sum = res.1
}

因为我需要根据索引测试属性。

编辑:

好吧,我终于想出了一些种解决方案,但看看它有多荒谬。必须有正确和适当的 ATS 方式来执行此操作。

#include "share/atspre_staload.hats"

val example = "1234567"

fn digit(c: char): int =
  case- c of
  | '0' => 0 | '1' => 1 | '2' => 2 | '3' => 3 | '4' => 4
  | '5' => 5 | '6' => 6 | '7' => 7 | '8' => 8 | '9' => 9

fn f(): int = loop(0, 0) where {
  fn get(i: int): char = loop(i, string2ptr(example)) where {
    fun loop(i: int, s: ptr): char =
      if i > 0 then loop(i-1, ptr0_succ<char>(s)) else
      $UNSAFE.ptr0_get<char>(s)
  }
  fun loop(i: int, acc: int): int =
    if get(i) = '\000' then acc else
    loop(i + 1, acc + digit(get(i)))
}

implement main0() = () where {
  val () = println!("f: ", f())
}

输出:

f: 28

EDIT2:

不那么荒谬:

...
  val p = string2ptr(example)
  fn get(i: int): char = $UNSAFE.ptr0_get<char>(add_ptr_bsz(p, g0int2uint(i) * sizeof<char>))
...

EDIT3:

我可以再次使用string[i]

overload + with add_ptr_bsz
fn string_get_at(str, i) = $UNSAFE.ptr0_get<charNZ>(string2ptr(str)+g0int2uint(i))
overload [] with string_get_at

这与我在 prelude/DATS/string.dats 中看到的几乎相同......有什么问题?

【问题讨论】:

  • 已批准编辑,但我怀疑提高可见性是否会带来任何好处。如果您不了解 ATS,就无法回答这个问题。

标签: string loops indexing ats


【解决方案1】:

好的,day2 的以下实现是安全的:

fn
day2
(input: string): uint = let

val
[n:int]
input = g1ofg0(input)

val n0 = strlen(input)
val n0 = sz2i(n0) // int(n)

fun
nextindex
(
 i: natLt(n)
) : natLt(n) = nmod(i + n0/2, n0)

fun
loop(i: natLte(n), acc: uint): uint =
  if i >= n0 then acc else
  (
    if input[i] = input[nextindex(i)]
      then loop(i+1, acc + digit2uint(input[i]))
      else loop(i+1, acc)
  )

in
  loop(0, 0u)
end // end of [day2]

【讨论】:

    【解决方案2】:

    这里有几个问题。你的函数f()可以写成:

    fn digit2int(c: char): int = (c - '0')
    fn f(): int = loop(example, 0, 0) where
    {
      fun
      loop
      {n:int}
      {i:nat|i <= n}
      (cs: string(n), i: int(i), acc: int): int =
      if
      string_is_atend(cs, i)
      then acc else loop(cs, i+1, acc+digit2int(cs[i]))
    }
    

    这种编程涉及依赖类型。它通常对程序员提出了更多要求。

    【讨论】:

      【解决方案3】:

      我尝试重新实现你的函数day2

      fn digit2int(c: char): int = (c - '0')
      
      fn
      day2(input: string): int =
        loop(0, 0) where
      {
        val n0 = strlen(input)
        val n0 =
        g0uint2int_size_int(n0)
        val p0 = string2ptr(input)
        fn nextindex(i: int): int = (i + n0/2) mod n0
        fun get(i: int): char = $UNSAFE.ptr0_get_at<char>(p0, i)
        fun loop(i: int, acc: int): int =
          if i >= n0 then acc else
          (
            if get(i) = get(nextindex(i))
              then loop(i+1, acc + digit2int(get(i))) else loop(i+1, acc)
          )
      }
      

      我不得不说上面的实现非常丑陋(而且风格非常不安全)。如果有时间,我稍后会尝试给出一个安全优雅的实现。

      【讨论】:

        【解决方案4】:

        我能够在提供的帮助下完成这项工作,但只是为了还有一个完整的功能示例,这是Day 1, 2017 Advent of Code的解决方案:

        /* compile with: patscc -O2 -D_GNU_SOURCE -DATS_MEMALLOC_LIBC day1.dats -o day1 */
        #include "share/atspre_staload.hats"
        #include "share/atspre_staload_libats_ML.hats"
        
        val input: string = "12341234" /* actual value provided by contest */
        
        fn digit(c: char): uint =
          case- c of
          | '0' => 0u | '1' => 1u | '2' => 2u | '3' => 3u | '4' => 4u
          | '5' => 5u | '6' => 6u | '7' => 7u | '8' => 8u | '9' => 9u
        
        typedef charint = (char, uint)
        
        fn part1(): uint = sum where {
          val lastchar = input[strlen(input)-1]
          val res = input.foldleft(TYPE{charint})((lastchar, 0u), (lam((last, sum): charint, c: char) =>
                        if last = c then (c, sum + digit(c)) else (c, sum)))
          val sum = res.1
        }
        
        typedef natLT(n:int) = [i:nat | i < n] int(i)
        typedef natLTe(n:int) = [i:nat | i <= n] int(i)
        
        fn part2(): uint = loop(0, 0u) where {
          val [n:int] input = g1ofg0(input)
          val len = sz2i(strlen(input))
          fn nextindex(i: natLT(n)): natLT(n) = nmod(i + len/2, len)
          fun loop(i: natLTe(n), acc: uint): uint =
            if i >= len then acc else
            if input[i] = input[nextindex(i)] then loop(i+1, acc + digit(input[i])) else
            loop(i+1, acc)
        }
        
        extern fun reset_timer(): void = "ext#reset_timer"
        extern fun elapsed_time(): double = "ext#elapsed_time"
        %{
        #include <sys/time.h>
        #include <time.h>
        struct timeval timer_timeval;
        void reset_timer() { gettimeofday(&timer_timeval, NULL); }
        double elapsed_time() {
          struct timeval now;
          gettimeofday(&now, NULL);
          int secs = now.tv_sec - timer_timeval.tv_sec;
          double ms = (now.tv_usec - timer_timeval.tv_usec) / ((double)1000000);
          return(secs + ms);
        }
        %}
        
        fn bench(f: () -> void) = () where {
          val () = reset_timer()
          val () = f()
          val c = elapsed_time()
          val () = println!(" (timing: ", c, ")")
        }
        
        implement main0() =
          begin
            bench(lam() => print!("part1: ", part1()));
            bench(lam() => print!("part2: ", part2()));
          end
        

        输出(对于假的“12341234”输入):

        part1: 0 (timing: 0.000137)
        part2: 20 (timing: 0.000001)
        

        【讨论】:

          猜你喜欢
          • 2014-04-02
          • 2021-02-04
          • 2016-01-09
          • 2013-05-29
          • 1970-01-01
          • 2010-11-21
          • 2018-11-19
          • 2023-02-11
          • 2018-05-15
          相关资源
          最近更新 更多